add economi system

This commit is contained in:
Arthur Puechberty
2026-01-17 18:52:16 +01:00
parent 6c9241f349
commit 7d706b4d79
20 changed files with 2123 additions and 9 deletions
+224
View File
@@ -0,0 +1,224 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder, PermissionFlagsBits } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "adminaddxp",
description: "Ajoute de l'XP ou des niveaux à un utilisateur (admin uniquement).",
aliases: ["addxp", "givexp", "addlevel"],
permissions: [PermissionFlagsBits.Administrator],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM levels_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) {
console.error(`DB error in guildCondition for guild ${guildId}`, err);
return resolve(false);
}
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [
{
type: "USER",
name: "utilisateur",
description: "L'utilisateur à qui ajouter l'XP ou les niveaux.",
required: true,
},
{
type: "STRING",
name: "type",
description: "Type d'ajout : 'xp' ou 'level'.",
required: true,
choices: [
{ name: "XP", value: "xp" },
{ name: "Niveau", value: "level" },
],
},
{
type: "INTEGER",
name: "quantite",
description: "Quantité d'XP ou de niveaux à ajouter.",
required: true,
},
],
executePrefix: async (client, message, args) => {
const guildId = message.guild.id;
// Vérifier les arguments : !adminaddxp @user xp/level quantité
if (args.length < 3) {
return message.reply("Usage : `!adminaddxp @utilisateur <xp|level> <quantité>`");
}
const userMention = message.mentions.users.first();
if (!userMention) {
return message.reply("Veuillez mentionner un utilisateur valide.");
}
const type = args[1].toLowerCase();
if (type !== "xp" && type !== "level") {
return message.reply("Le type doit être `xp` ou `level`.");
}
const quantite = parseInt(args[2], 10);
if (isNaN(quantite) || quantite <= 0) {
return message.reply("La quantité doit être un nombre positif.");
}
await processAddXp(guildId, userMention.id, type, quantite, message.guild, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
const guildId = interaction.guild.id;
const user = interaction.options.getUser("utilisateur");
const type = interaction.options.getString("type").toLowerCase();
const quantite = interaction.options.getInteger("quantite");
if (type !== "xp" && type !== "level") {
return interaction.reply({ content: "Le type doit être `xp` ou `level`.", ephemeral: true });
}
if (quantite <= 0) {
return interaction.reply({ content: "La quantité doit être un nombre positif.", ephemeral: true });
}
await processAddXp(guildId, user.id, type, quantite, interaction.guild, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function processAddXp(guildId, userId, type, quantite, guild, onSuccess, onError) {
// Récupérer la config des niveaux
db.get(
`SELECT
xp_courbe_type,
multiplier_courbe_for_level,
level_max,
level_announcements_enabled,
level_announcements_channel_id,
level_announcements_message,
level_annoncement_every_level
FROM levels_config WHERE guild_id = ?`,
[guildId],
(err, config) => {
if (err || !config) {
return onError("Le système de niveaux n'est pas configuré sur ce serveur.");
}
// Récupérer les données actuelles de l'utilisateur
db.get(
`SELECT xp, level FROM user_levels WHERE guild_id = ? AND user_id = ?`,
[guildId, userId],
(err, userRow) => {
if (err) {
return onError("Erreur lors de la récupération des données de l'utilisateur.");
}
let currentXp = userRow ? userRow.xp : 0;
let currentLevel = userRow ? userRow.level : 1;
// Fonction de courbe
const multiplier = config.multiplier_courbe_for_level;
let fonction_courbe;
if (config.xp_courbe_type === "constante") {
fonction_courbe = (level) => multiplier;
} else if (config.xp_courbe_type === "linear") {
fonction_courbe = (level) => level * multiplier;
} else if (config.xp_courbe_type === "quadratic") {
fonction_courbe = (level) => level * level * multiplier;
} else if (config.xp_courbe_type === "exponential") {
fonction_courbe = (level) => Math.pow(2, level - 1) * multiplier;
}
let newXp = currentXp;
let newLevel = currentLevel;
if (type === "xp") {
newXp += quantite;
} else if (type === "level") {
// Ajouter les niveaux directement
for (let i = 0; i < quantite; i++) {
if (config.level_max === 0 || newLevel < config.level_max) {
newLevel += 1;
}
}
newXp = 0; // Reset XP au début du nouveau niveau
}
// Recalculer les niveaux si on a ajouté de l'XP
if (type === "xp") {
let xpForNextLevel = fonction_courbe(newLevel);
while (newXp >= xpForNextLevel && (config.level_max === 0 || newLevel < config.level_max)) {
newXp -= xpForNextLevel;
newLevel += 1;
xpForNextLevel = fonction_courbe(newLevel);
// Annonce si activée
if (config.level_announcements_enabled && (newLevel % config.level_annoncement_every_level === 0)) {
const channel = guild.channels.cache.get(config.level_announcements_channel_id);
if (channel) {
const member = guild.members.cache.get(userId);
let announcementMsg = config.level_announcements_message;
announcementMsg = announcementMsg
.replace("{user}", member?.user?.username || "Utilisateur")
.replace("{mention}", `<@${userId}>`)
.replace("{level}", newLevel)
.replace("{level-xp}", fonction_courbe(newLevel));
channel.send(announcementMsg);
}
}
}
}
// Sauvegarder en base
db.run(
`INSERT INTO user_levels (guild_id, user_id, xp, level)
VALUES (?, ?, ?, ?)
ON CONFLICT(guild_id, user_id) DO UPDATE SET
xp = excluded.xp,
level = excluded.level`,
[guildId, userId, newXp, newLevel],
(err) => {
if (err) {
return onError("Erreur lors de la sauvegarde des données.");
}
const embed = new EmbedBuilder()
.setTitle("✅ XP/Niveau ajouté")
.setColor("#00FF00")
.setDescription(
type === "xp"
? `**${quantite} XP** ajouté à <@${userId}>.\n\n` +
`**Niveau actuel :** ${newLevel}\n**XP actuel :** ${newXp}`
: `**${quantite} niveau(x)** ajouté(s) à <@${userId}>.\n\n` +
`**Niveau actuel :** ${newLevel}\n**XP actuel :** ${newXp}`
)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
);
}
+108
View File
@@ -0,0 +1,108 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder, PermissionFlagsBits } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "adminecoadd",
description: "Ajoute de l'argent à un utilisateur (admin uniquement).",
aliases: ["addmoney", "givemoney"],
permissions: [PermissionFlagsBits.Administrator],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM economy_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) return resolve(false);
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [
{
type: "USER",
name: "utilisateur",
description: "L'utilisateur à qui ajouter l'argent.",
required: true,
},
{
type: "INTEGER",
name: "montant",
description: "Le montant à ajouter.",
required: true,
},
],
executePrefix: async (client, message, args) => {
const targetUser = message.mentions.users.first();
if (!targetUser) return message.reply("Veuillez mentionner un utilisateur.");
const amount = parseInt(args[1], 10);
if (isNaN(amount) || amount <= 0) return message.reply("Veuillez entrer un montant valide.");
await processAdminAdd(message.guild.id, targetUser, amount, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
const targetUser = interaction.options.getUser("utilisateur");
const amount = interaction.options.getInteger("montant");
if (amount <= 0) {
return interaction.reply({ content: "Le montant doit être positif.", ephemeral: true });
}
await processAdminAdd(interaction.guild.id, targetUser, amount, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function processAdminAdd(guildId, user, amount, onSuccess, onError) {
db.get(
"SELECT currency_name, currency_symbol FROM economy_config WHERE guild_id = ?",
[guildId],
(err, config) => {
if (err || !config) return onError("Le système d'économie n'est pas configuré.");
db.get(
"SELECT balance FROM user_economy WHERE guild_id = ? AND user_id = ?",
[guildId, user.id],
(err, row) => {
if (err) return onError("Erreur lors de la récupération des données.");
const newBalance = (row?.balance || 0) + amount;
db.run(
`INSERT INTO user_economy (guild_id, user_id, balance, bank)
VALUES (?, ?, ?, 0)
ON CONFLICT(guild_id, user_id) DO UPDATE SET balance = ?`,
[guildId, user.id, newBalance, newBalance],
(err) => {
if (err) return onError("Erreur lors de la sauvegarde.");
const embed = new EmbedBuilder()
.setTitle(`${config.currency_symbol} Argent ajouté`)
.setColor("#00FF00")
.setDescription(`**${amount.toLocaleString()} ${config.currency_name}** ajouté à ${user}.\n\nNouveau solde : **${newBalance.toLocaleString()} ${config.currency_name}**`)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
);
}
+108
View File
@@ -0,0 +1,108 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder, PermissionFlagsBits } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "adminecoremove",
description: "Retire de l'argent à un utilisateur (admin uniquement).",
aliases: ["removemoney", "takemoney"],
permissions: [PermissionFlagsBits.Administrator],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM economy_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) return resolve(false);
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [
{
type: "USER",
name: "utilisateur",
description: "L'utilisateur à qui retirer l'argent.",
required: true,
},
{
type: "INTEGER",
name: "montant",
description: "Le montant à retirer.",
required: true,
},
],
executePrefix: async (client, message, args) => {
const targetUser = message.mentions.users.first();
if (!targetUser) return message.reply("Veuillez mentionner un utilisateur.");
const amount = parseInt(args[1], 10);
if (isNaN(amount) || amount <= 0) return message.reply("Veuillez entrer un montant valide.");
await processAdminRemove(message.guild.id, targetUser, amount, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
const targetUser = interaction.options.getUser("utilisateur");
const amount = interaction.options.getInteger("montant");
if (amount <= 0) {
return interaction.reply({ content: "Le montant doit être positif.", ephemeral: true });
}
await processAdminRemove(interaction.guild.id, targetUser, amount, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function processAdminRemove(guildId, user, amount, onSuccess, onError) {
db.get(
"SELECT currency_name, currency_symbol FROM economy_config WHERE guild_id = ?",
[guildId],
(err, config) => {
if (err || !config) return onError("Le système d'économie n'est pas configuré.");
db.get(
"SELECT balance FROM user_economy WHERE guild_id = ? AND user_id = ?",
[guildId, user.id],
(err, row) => {
if (err) return onError("Erreur lors de la récupération des données.");
const newBalance = Math.max(0, (row?.balance || 0) - amount);
db.run(
`INSERT INTO user_economy (guild_id, user_id, balance, bank)
VALUES (?, ?, ?, 0)
ON CONFLICT(guild_id, user_id) DO UPDATE SET balance = ?`,
[guildId, user.id, newBalance, newBalance],
(err) => {
if (err) return onError("Erreur lors de la sauvegarde.");
const embed = new EmbedBuilder()
.setTitle(`${config.currency_symbol} Argent retiré`)
.setColor("#FF0000")
.setDescription(`**${amount.toLocaleString()} ${config.currency_name}** retiré à ${user}.\n\nNouveau solde : **${newBalance.toLocaleString()} ${config.currency_name}**`)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
);
}
+198
View File
@@ -0,0 +1,198 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder, PermissionFlagsBits } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "adminremovexp",
description: "Retire de l'XP ou des niveaux à un utilisateur (admin uniquement).",
aliases: ["removexp", "takexp", "removelevel"],
permissions: [PermissionFlagsBits.Administrator],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM levels_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) {
console.error(`DB error in guildCondition for guild ${guildId}`, err);
return resolve(false);
}
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [
{
type: "USER",
name: "utilisateur",
description: "L'utilisateur à qui retirer l'XP ou les niveaux.",
required: true,
},
{
type: "STRING",
name: "type",
description: "Type de retrait : 'xp' ou 'level'.",
required: true,
choices: [
{ name: "XP", value: "xp" },
{ name: "Niveau", value: "level" },
],
},
{
type: "INTEGER",
name: "quantite",
description: "Quantité d'XP ou de niveaux à retirer.",
required: true,
},
],
executePrefix: async (client, message, args) => {
const guildId = message.guild.id;
// Vérifier les arguments : !adminremovexp @user xp/level quantité
if (args.length < 3) {
return message.reply("Usage : `!adminremovexp @utilisateur <xp|level> <quantité>`");
}
const userMention = message.mentions.users.first();
if (!userMention) {
return message.reply("Veuillez mentionner un utilisateur valide.");
}
const type = args[1].toLowerCase();
if (type !== "xp" && type !== "level") {
return message.reply("Le type doit être `xp` ou `level`.");
}
const quantite = parseInt(args[2], 10);
if (isNaN(quantite) || quantite <= 0) {
return message.reply("La quantité doit être un nombre positif.");
}
await processRemoveXp(guildId, userMention.id, type, quantite, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
const guildId = interaction.guild.id;
const user = interaction.options.getUser("utilisateur");
const type = interaction.options.getString("type");
const quantite = interaction.options.getInteger("quantite");
if (quantite <= 0) {
return interaction.reply({ content: "La quantité doit être un nombre positif.", ephemeral: true });
}
await processRemoveXp(guildId, user.id, type, quantite, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function processRemoveXp(guildId, userId, type, quantite, onSuccess, onError) {
// Récupérer la config des niveaux
db.get(
`SELECT
xp_courbe_type,
multiplier_courbe_for_level
FROM levels_config WHERE guild_id = ?`,
[guildId],
(err, config) => {
if (err || !config) {
return onError("Le système de niveaux n'est pas configuré sur ce serveur.");
}
// Récupérer les données actuelles de l'utilisateur
db.get(
`SELECT xp, level FROM user_levels WHERE guild_id = ? AND user_id = ?`,
[guildId, userId],
(err, userRow) => {
if (err) {
return onError("Erreur lors de la récupération des données de l'utilisateur.");
}
if (!userRow) {
return onError("Cet utilisateur n'a pas encore de données de niveau.");
}
let currentXp = userRow.xp;
let currentLevel = userRow.level;
// Fonction de courbe
const multiplier = config.multiplier_courbe_for_level;
let fonction_courbe;
if (config.xp_courbe_type === "constante") {
fonction_courbe = (level) => multiplier;
} else if (config.xp_courbe_type === "linear") {
fonction_courbe = (level) => level * multiplier;
} else if (config.xp_courbe_type === "quadratic") {
fonction_courbe = (level) => level * level * multiplier;
} else if (config.xp_courbe_type === "exponential") {
fonction_courbe = (level) => Math.pow(2, level - 1) * multiplier;
}
let newXp = currentXp;
let newLevel = currentLevel;
if (type === "xp") {
newXp -= quantite;
// Gérer les niveaux en négatif
while (newXp < 0 && newLevel > 1) {
newLevel -= 1;
newXp += fonction_courbe(newLevel);
}
// S'assurer que l'XP ne soit pas négatif
if (newXp < 0) {
newXp = 0;
}
} else if (type === "level") {
// Retirer les niveaux directement
newLevel -= quantite;
if (newLevel < 1) {
newLevel = 1;
}
newXp = 0; // Reset XP
}
// Sauvegarder en base
db.run(
`UPDATE user_levels SET xp = ?, level = ? WHERE guild_id = ? AND user_id = ?`,
[newXp, newLevel, guildId, userId],
(err) => {
if (err) {
return onError("Erreur lors de la sauvegarde des données.");
}
const embed = new EmbedBuilder()
.setTitle("❌ XP/Niveau retiré")
.setColor("#FF0000")
.setDescription(
type === "xp"
? `**${quantite} XP** retiré à <@${userId}>.\n\n` +
`**Niveau actuel :** ${newLevel}\n**XP actuel :** ${newXp}`
: `**${quantite} niveau(x)** retiré(s) à <@${userId}>.\n\n` +
`**Niveau actuel :** ${newLevel}\n**XP actuel :** ${newXp}`
)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
);
}
+187
View File
@@ -0,0 +1,187 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder, PermissionFlagsBits } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "adminsetxp",
description: "Définit l'XP ou le niveau d'un utilisateur (admin uniquement).",
aliases: ["setxp", "setlevel"],
permissions: [PermissionFlagsBits.Administrator],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM levels_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) {
console.error(`DB error in guildCondition for guild ${guildId}`, err);
return resolve(false);
}
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [
{
type: "USER",
name: "utilisateur",
description: "L'utilisateur dont définir l'XP ou le niveau.",
required: true,
},
{
type: "STRING",
name: "type",
description: "Ce que vous voulez définir : 'xp' ou 'level'.",
required: true,
choices: [
{ name: "XP", value: "xp" },
{ name: "Niveau", value: "level" },
],
},
{
type: "INTEGER",
name: "valeur",
description: "La nouvelle valeur d'XP ou de niveau.",
required: true,
},
],
executePrefix: async (client, message, args) => {
const guildId = message.guild.id;
// Vérifier les arguments : !adminsetxp @user xp/level valeur
if (args.length < 3) {
return message.reply("Usage : `!adminsetxp @utilisateur <xp|level> <valeur>`");
}
const userMention = message.mentions.users.first();
if (!userMention) {
return message.reply("Veuillez mentionner un utilisateur valide.");
}
const type = args[1].toLowerCase();
if (type !== "xp" && type !== "level") {
return message.reply("Le type doit être `xp` ou `level`.");
}
const valeur = parseInt(args[2], 10);
if (isNaN(valeur) || valeur < 0) {
return message.reply("La valeur doit être un nombre positif ou zéro.");
}
await processSetXp(guildId, userMention.id, type, valeur, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
const guildId = interaction.guild.id;
const user = interaction.options.getUser("utilisateur");
const type = interaction.options.getString("type");
const valeur = interaction.options.getInteger("valeur");
if (valeur < 0) {
return interaction.reply({ content: "La valeur doit être un nombre positif ou zéro.", ephemeral: true });
}
await processSetXp(guildId, user.id, type, valeur, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function processSetXp(guildId, userId, type, valeur, onSuccess, onError) {
// Récupérer la config des niveaux
db.get(
`SELECT
xp_courbe_type,
multiplier_courbe_for_level,
level_max
FROM levels_config WHERE guild_id = ?`,
[guildId],
(err, config) => {
if (err || !config) {
return onError("Le système de niveaux n'est pas configuré sur ce serveur.");
}
// Fonction de courbe
const multiplier = config.multiplier_courbe_for_level;
let fonction_courbe;
if (config.xp_courbe_type === "constante") {
fonction_courbe = (level) => multiplier;
} else if (config.xp_courbe_type === "linear") {
fonction_courbe = (level) => level * multiplier;
} else if (config.xp_courbe_type === "quadratic") {
fonction_courbe = (level) => level * level * multiplier;
} else if (config.xp_courbe_type === "exponential") {
fonction_courbe = (level) => Math.pow(2, level - 1) * multiplier;
}
let newXp, newLevel;
if (type === "xp") {
// Définir l'XP et recalculer le niveau
newXp = valeur;
newLevel = 1;
let xpForNextLevel = fonction_courbe(newLevel);
while (newXp >= xpForNextLevel && (config.level_max === 0 || newLevel < config.level_max)) {
newXp -= xpForNextLevel;
newLevel += 1;
xpForNextLevel = fonction_courbe(newLevel);
}
} else if (type === "level") {
// Définir le niveau directement
newLevel = valeur;
if (newLevel < 1) {
newLevel = 1;
}
if (config.level_max > 0 && newLevel > config.level_max) {
newLevel = config.level_max;
}
newXp = 0; // Reset XP au début du niveau
}
// Sauvegarder en base (INSERT ou UPDATE)
db.run(
`INSERT INTO user_levels (guild_id, user_id, xp, level)
VALUES (?, ?, ?, ?)
ON CONFLICT(guild_id, user_id) DO UPDATE SET
xp = excluded.xp,
level = excluded.level`,
[guildId, userId, newXp, newLevel],
(err) => {
if (err) {
return onError("Erreur lors de la sauvegarde des données.");
}
const embed = new EmbedBuilder()
.setTitle("⚙️ XP/Niveau défini")
.setColor("#0099FF")
.setDescription(
type === "xp"
? `L'XP de <@${userId}> a été défini.\n\n` +
`**Niveau actuel :** ${newLevel}\n**XP actuel :** ${newXp}`
: `Le niveau de <@${userId}> a été défini à **${newLevel}**.\n\n` +
`**XP actuel :** ${newXp}`
)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
+99
View File
@@ -0,0 +1,99 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "balance",
description: "Affiche votre solde ou celui d'un autre utilisateur.",
aliases: ["bal", "money", "coins", "solde"],
permissions: [],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM economy_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) {
console.error(`DB error in guildCondition for guild ${guildId}`, err);
return resolve(false);
}
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [
{
type: "USER",
name: "utilisateur",
description: "L'utilisateur dont afficher le solde.",
required: false,
},
],
executePrefix: async (client, message, args) => {
const guildId = message.guild.id;
const targetUser = message.mentions.users.first() || message.author;
await showBalance(guildId, targetUser, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
const guildId = interaction.guild.id;
const targetUser = interaction.options.getUser("utilisateur") || interaction.user;
await showBalance(guildId, targetUser, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function showBalance(guildId, user, onSuccess, onError) {
db.get(
"SELECT currency_name, currency_symbol FROM economy_config WHERE guild_id = ?",
[guildId],
(err, config) => {
if (err || !config) {
return onError("Le système d'économie n'est pas configuré sur ce serveur.");
}
db.get(
"SELECT balance, bank FROM user_economy WHERE guild_id = ? AND user_id = ?",
[guildId, user.id],
(err, row) => {
if (err) {
return onError("Erreur lors de la récupération du solde.");
}
const balance = row?.balance || 0;
const bank = row?.bank || 0;
const total = balance + bank;
const embed = new EmbedBuilder()
.setTitle(`${config.currency_symbol} Solde de ${user.username}`)
.setColor("#FFD700")
.setThumbnail(user.displayAvatarURL())
.addFields(
{ name: "💵 Portefeuille", value: `${balance.toLocaleString()} ${config.currency_name}`, inline: true },
{ name: "🏦 Banque", value: `${bank.toLocaleString()} ${config.currency_name}`, inline: true },
{ name: "💰 Total", value: `${total.toLocaleString()} ${config.currency_name}`, inline: false }
)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
+138
View File
@@ -0,0 +1,138 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder } = require("discord.js");
const db = require("../db");
const crimeSuccessMessages = [
"Vous avez cambriolé une maison et volé",
"Vous avez piraté un distributeur et récupéré",
"Vous avez arnaqué quelqu'un et obtenu",
"Vous avez trouvé un portefeuille abandonné contenant",
"Vous avez vendu des objets volés pour",
"Vous avez braqué une épicerie et pris",
"Vous avez piraté un compte bancaire et transféré"
];
const crimeFailMessages = [
"Vous vous êtes fait attraper par la police !",
"Un témoin vous a dénoncé !",
"Les caméras de surveillance vous ont repéré !",
"Vous avez glissé sur une peau de banane en fuyant !",
"Un chien de garde vous a mordu !",
"Vous avez déclenché l'alarme !",
"La victime connaissait du karaté !"
];
module.exports = addCommand({
name: "crime",
description: "Tentez un crime risqué pour de l'argent.",
aliases: ["vol", "steal", "rob"],
permissions: [],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM economy_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) return resolve(false);
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [],
executePrefix: async (client, message, args) => {
await doCrime(message.guild.id, message.author, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
await doCrime(interaction.guild.id, interaction.user, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function doCrime(guildId, user, onSuccess, onError) {
db.get(
`SELECT currency_name, currency_symbol, crime_min_amount, crime_max_amount,
crime_success_rate, crime_fine_percent, crime_cooldown_minutes
FROM economy_config WHERE guild_id = ?`,
[guildId],
(err, config) => {
if (err || !config) {
return onError("Le système d'économie n'est pas configuré.");
}
db.get(
"SELECT balance, last_crime_timestamp FROM user_economy WHERE guild_id = ? AND user_id = ?",
[guildId, user.id],
(err, row) => {
if (err) return onError("Erreur lors de la récupération des données.");
const now = Date.now();
const cooldownMs = config.crime_cooldown_minutes * 60 * 1000;
const lastCrime = row?.last_crime_timestamp || 0;
if (now - lastCrime < cooldownMs) {
const timeLeft = cooldownMs - (now - lastCrime);
const minutes = Math.floor(timeLeft / (60 * 1000));
const seconds = Math.floor((timeLeft % (60 * 1000)) / 1000);
return onError(`⏰ Vous devez attendre encore **${minutes}m ${seconds}s** avant de pouvoir commettre un autre crime.`);
}
const currentBalance = row?.balance || 0;
const success = Math.random() * 100 < config.crime_success_rate;
let newBalance;
let embed;
if (success) {
const earned = Math.floor(Math.random() * (config.crime_max_amount - config.crime_min_amount + 1)) + config.crime_min_amount;
newBalance = currentBalance + earned;
const crimeMsg = crimeSuccessMessages[Math.floor(Math.random() * crimeSuccessMessages.length)];
embed = new EmbedBuilder()
.setTitle(`${config.currency_symbol} Crime réussi !`)
.setColor("#00FF00")
.setDescription(`${crimeMsg} **${earned.toLocaleString()} ${config.currency_name}** !`)
.setTimestamp();
} else {
const fine = Math.floor(currentBalance * (config.crime_fine_percent / 100));
newBalance = Math.max(0, currentBalance - fine);
const failMsg = crimeFailMessages[Math.floor(Math.random() * crimeFailMessages.length)];
embed = new EmbedBuilder()
.setTitle("🚔 Crime échoué !")
.setColor("#FF0000")
.setDescription(`${failMsg}\n\nVous avez payé une amende de **${fine.toLocaleString()} ${config.currency_name}**.`)
.setTimestamp();
}
db.run(
`INSERT INTO user_economy (guild_id, user_id, balance, bank, last_crime_timestamp)
VALUES (?, ?, ?, 0, ?)
ON CONFLICT(guild_id, user_id) DO UPDATE SET
balance = ?,
last_crime_timestamp = ?`,
[guildId, user.id, newBalance, now, newBalance, now],
(err) => {
if (err) return onError("Erreur lors de la sauvegarde.");
onSuccess(embed);
}
);
}
);
}
);
}
+97
View File
@@ -0,0 +1,97 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "daily",
description: "Récupérez votre récompense quotidienne.",
aliases: ["quotidien", "journalier"],
permissions: [],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM economy_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) return resolve(false);
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [],
executePrefix: async (client, message, args) => {
await claimDaily(message.guild.id, message.author, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
await claimDaily(interaction.guild.id, interaction.user, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function claimDaily(guildId, user, onSuccess, onError) {
db.get(
"SELECT currency_name, currency_symbol, daily_amount, daily_cooldown_hours FROM economy_config WHERE guild_id = ?",
[guildId],
(err, config) => {
if (err || !config) {
return onError("Le système d'économie n'est pas configuré.");
}
db.get(
"SELECT balance, last_daily_timestamp FROM user_economy WHERE guild_id = ? AND user_id = ?",
[guildId, user.id],
(err, row) => {
if (err) return onError("Erreur lors de la récupération des données.");
const now = Date.now();
const cooldownMs = config.daily_cooldown_hours * 60 * 60 * 1000;
const lastDaily = row?.last_daily_timestamp || 0;
if (now - lastDaily < cooldownMs) {
const timeLeft = cooldownMs - (now - lastDaily);
const hours = Math.floor(timeLeft / (60 * 60 * 1000));
const minutes = Math.floor((timeLeft % (60 * 60 * 1000)) / (60 * 1000));
return onError(`⏰ Vous devez attendre encore **${hours}h ${minutes}m** avant de récupérer votre récompense quotidienne.`);
}
const newBalance = (row?.balance || 0) + config.daily_amount;
db.run(
`INSERT INTO user_economy (guild_id, user_id, balance, bank, last_daily_timestamp)
VALUES (?, ?, ?, 0, ?)
ON CONFLICT(guild_id, user_id) DO UPDATE SET
balance = ?,
last_daily_timestamp = ?`,
[guildId, user.id, newBalance, now, newBalance, now],
(err) => {
if (err) return onError("Erreur lors de la sauvegarde.");
const embed = new EmbedBuilder()
.setTitle(`${config.currency_symbol} Récompense quotidienne`)
.setColor("#00FF00")
.setDescription(`Vous avez récupéré **${config.daily_amount.toLocaleString()} ${config.currency_name}** !\n\nNouveau solde : **${newBalance.toLocaleString()} ${config.currency_name}**`)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
);
}
+118
View File
@@ -0,0 +1,118 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "deposit",
description: "Déposez de l'argent à la banque.",
aliases: ["dep", "deposer"],
permissions: [],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM economy_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) return resolve(false);
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [
{
type: "STRING",
name: "montant",
description: "Le montant à déposer (nombre ou 'all').",
required: true,
},
],
executePrefix: async (client, message, args) => {
if (!args[0]) return message.reply("Usage : `!deposit <montant|all>`");
await processDeposit(message.guild.id, message.author.id, args[0], (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
const montant = interaction.options.getString("montant");
await processDeposit(interaction.guild.id, interaction.user.id, montant, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function processDeposit(guildId, userId, amountStr, onSuccess, onError) {
db.get(
"SELECT currency_name, currency_symbol FROM economy_config WHERE guild_id = ?",
[guildId],
(err, config) => {
if (err || !config) return onError("Le système d'économie n'est pas configuré.");
db.get(
"SELECT balance, bank FROM user_economy WHERE guild_id = ? AND user_id = ?",
[guildId, userId],
(err, row) => {
if (err) return onError("Erreur lors de la récupération des données.");
const currentBalance = row?.balance || 0;
const currentBank = row?.bank || 0;
let amount;
if (amountStr.toLowerCase() === "all") {
amount = currentBalance;
} else {
amount = parseInt(amountStr, 10);
}
if (isNaN(amount) || amount <= 0) {
return onError("Veuillez entrer un montant valide.");
}
if (amount > currentBalance) {
return onError(`Vous n'avez pas assez d'argent. Solde actuel : **${currentBalance.toLocaleString()} ${config.currency_name}**`);
}
const newBalance = currentBalance - amount;
const newBank = currentBank + amount;
db.run(
`INSERT INTO user_economy (guild_id, user_id, balance, bank)
VALUES (?, ?, ?, ?)
ON CONFLICT(guild_id, user_id) DO UPDATE SET
balance = ?,
bank = ?`,
[guildId, userId, newBalance, newBank, newBalance, newBank],
(err) => {
if (err) return onError("Erreur lors de la sauvegarde.");
const embed = new EmbedBuilder()
.setTitle(`🏦 Dépôt effectué`)
.setColor("#00FF00")
.setDescription(`Vous avez déposé **${amount.toLocaleString()} ${config.currency_name}** à la banque.`)
.addFields(
{ name: "💵 Portefeuille", value: `${newBalance.toLocaleString()} ${config.currency_name}`, inline: true },
{ name: "🏦 Banque", value: `${newBank.toLocaleString()} ${config.currency_name}`, inline: true }
)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
);
}
+147
View File
@@ -0,0 +1,147 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "pay",
description: "Envoyez de l'argent à un autre utilisateur.",
aliases: ["give", "transfer", "envoyer"],
permissions: [],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM economy_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) return resolve(false);
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [
{
type: "USER",
name: "utilisateur",
description: "L'utilisateur à qui envoyer l'argent.",
required: true,
},
{
type: "INTEGER",
name: "montant",
description: "Le montant à envoyer.",
required: true,
},
],
executePrefix: async (client, message, args) => {
const targetUser = message.mentions.users.first();
if (!targetUser) return message.reply("Veuillez mentionner un utilisateur.");
const amount = parseInt(args[1], 10);
if (isNaN(amount) || amount <= 0) return message.reply("Veuillez entrer un montant valide.");
await processPay(message.guild.id, message.author, targetUser, amount, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
const targetUser = interaction.options.getUser("utilisateur");
const amount = interaction.options.getInteger("montant");
if (amount <= 0) {
return interaction.reply({ content: "Le montant doit être positif.", ephemeral: true });
}
await processPay(interaction.guild.id, interaction.user, targetUser, amount, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function processPay(guildId, sender, receiver, amount, onSuccess, onError) {
if (sender.id === receiver.id) {
return onError("Vous ne pouvez pas vous envoyer de l'argent à vous-même.");
}
if (receiver.bot) {
return onError("Vous ne pouvez pas envoyer de l'argent à un bot.");
}
db.get(
"SELECT currency_name, currency_symbol FROM economy_config WHERE guild_id = ?",
[guildId],
(err, config) => {
if (err || !config) return onError("Le système d'économie n'est pas configuré.");
db.get(
"SELECT balance FROM user_economy WHERE guild_id = ? AND user_id = ?",
[guildId, sender.id],
(err, senderRow) => {
if (err) return onError("Erreur lors de la récupération des données.");
const senderBalance = senderRow?.balance || 0;
if (amount > senderBalance) {
return onError(`Vous n'avez pas assez d'argent. Solde : **${senderBalance.toLocaleString()} ${config.currency_name}**`);
}
db.get(
"SELECT balance FROM user_economy WHERE guild_id = ? AND user_id = ?",
[guildId, receiver.id],
(err, receiverRow) => {
if (err) return onError("Erreur lors de la récupération des données.");
const receiverBalance = receiverRow?.balance || 0;
const newSenderBalance = senderBalance - amount;
const newReceiverBalance = receiverBalance + amount;
// Update sender
db.run(
`INSERT INTO user_economy (guild_id, user_id, balance, bank)
VALUES (?, ?, ?, 0)
ON CONFLICT(guild_id, user_id) DO UPDATE SET balance = ?`,
[guildId, sender.id, newSenderBalance, newSenderBalance],
(err) => {
if (err) return onError("Erreur lors de la sauvegarde.");
// Update receiver
db.run(
`INSERT INTO user_economy (guild_id, user_id, balance, bank)
VALUES (?, ?, ?, 0)
ON CONFLICT(guild_id, user_id) DO UPDATE SET balance = ?`,
[guildId, receiver.id, newReceiverBalance, newReceiverBalance],
(err) => {
if (err) return onError("Erreur lors de la sauvegarde.");
const embed = new EmbedBuilder()
.setTitle(`${config.currency_symbol} Transfert effectué`)
.setColor("#00FF00")
.setDescription(`Vous avez envoyé **${amount.toLocaleString()} ${config.currency_name}** à ${receiver}.`)
.addFields(
{ name: "Votre nouveau solde", value: `${newSenderBalance.toLocaleString()} ${config.currency_name}`, inline: true }
)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
);
}
);
}
);
}
+82
View File
@@ -0,0 +1,82 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "richest",
description: "Affiche le classement des plus riches du serveur.",
aliases: ["baltop", "leaderboard", "lb", "moneytop"],
permissions: [],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM economy_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) return resolve(false);
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [],
executePrefix: async (client, message, args) => {
await showLeaderboard(message.guild.id, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
await showLeaderboard(interaction.guild.id, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function showLeaderboard(guildId, onSuccess, onError) {
db.get(
"SELECT currency_name, currency_symbol FROM economy_config WHERE guild_id = ?",
[guildId],
(err, config) => {
if (err || !config) return onError("Le système d'économie n'est pas configuré.");
db.all(
`SELECT user_id, (balance + bank) as total FROM user_economy
WHERE guild_id = ? ORDER BY total DESC LIMIT 10`,
[guildId],
(err, rows) => {
if (err) return onError("Erreur lors de la récupération du classement.");
if (!rows || rows.length === 0) {
return onError("Aucun utilisateur n'a encore d'argent sur ce serveur.");
}
const embed = new EmbedBuilder()
.setTitle(`${config.currency_symbol} Top 10 des plus riches`)
.setColor("#FFD700")
.setDescription(
rows
.map((row, index) => {
const medal = index === 0 ? "🥇" : index === 1 ? "🥈" : index === 2 ? "🥉" : `**${index + 1}.**`;
return `${medal} <@${row.user_id}> - **${row.total.toLocaleString()}** ${config.currency_name}`;
})
.join("\n")
)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
+114
View File
@@ -0,0 +1,114 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder } = require("discord.js");
const db = require("../db");
module.exports = addCommand({
name: "withdraw",
description: "Retirez de l'argent de la banque.",
aliases: ["with", "retirer"],
permissions: [],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM economy_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) return resolve(false);
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [
{
type: "STRING",
name: "montant",
description: "Le montant à retirer (nombre ou 'all').",
required: true,
},
],
executePrefix: async (client, message, args) => {
if (!args[0]) return message.reply("Usage : `!withdraw <montant|all>`");
await processWithdraw(message.guild.id, message.author.id, args[0], (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
const montant = interaction.options.getString("montant");
await processWithdraw(interaction.guild.id, interaction.user.id, montant, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function processWithdraw(guildId, userId, amountStr, onSuccess, onError) {
db.get(
"SELECT currency_name, currency_symbol FROM economy_config WHERE guild_id = ?",
[guildId],
(err, config) => {
if (err || !config) return onError("Le système d'économie n'est pas configuré.");
db.get(
"SELECT balance, bank FROM user_economy WHERE guild_id = ? AND user_id = ?",
[guildId, userId],
(err, row) => {
if (err) return onError("Erreur lors de la récupération des données.");
const currentBalance = row?.balance || 0;
const currentBank = row?.bank || 0;
let amount;
if (amountStr.toLowerCase() === "all") {
amount = currentBank;
} else {
amount = parseInt(amountStr, 10);
}
if (isNaN(amount) || amount <= 0) {
return onError("Veuillez entrer un montant valide.");
}
if (amount > currentBank) {
return onError(`Vous n'avez pas assez d'argent en banque. Solde banque : **${currentBank.toLocaleString()} ${config.currency_name}**`);
}
const newBalance = currentBalance + amount;
const newBank = currentBank - amount;
db.run(
`UPDATE user_economy SET balance = ?, bank = ? WHERE guild_id = ? AND user_id = ?`,
[newBalance, newBank, guildId, userId],
(err) => {
if (err) return onError("Erreur lors de la sauvegarde.");
const embed = new EmbedBuilder()
.setTitle(`🏦 Retrait effectué`)
.setColor("#00FF00")
.setDescription(`Vous avez retiré **${amount.toLocaleString()} ${config.currency_name}** de la banque.`)
.addFields(
{ name: "💵 Portefeuille", value: `${newBalance.toLocaleString()} ${config.currency_name}`, inline: true },
{ name: "🏦 Banque", value: `${newBank.toLocaleString()} ${config.currency_name}`, inline: true }
)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
);
}
+114
View File
@@ -0,0 +1,114 @@
const addCommand = require("../fonctions/addCommand");
const { EmbedBuilder } = require("discord.js");
const db = require("../db");
const workMessages = [
"Vous avez travaillé comme serveur et gagné",
"Vous avez tondu des pelouses et gagné",
"Vous avez livré des pizzas et gagné",
"Vous avez aidé un voisin à déménager et gagné",
"Vous avez fait du baby-sitting et gagné",
"Vous avez lavé des voitures et gagné",
"Vous avez promené des chiens et gagné",
"Vous avez vendu des limonades et gagné",
"Vous avez travaillé comme caissier et gagné",
"Vous avez donné des cours particuliers et gagné",
"Vous avez réparé des ordinateurs et gagné",
"Vous avez fait du jardinage et gagné"
];
module.exports = addCommand({
name: "work",
description: "Travaillez pour gagner de l'argent.",
aliases: ["travail", "travailler", "job"],
permissions: [],
botOwnerOnly: false,
dm: false,
scope: "guild",
guildCondition: async (guildId) => {
return new Promise((resolve) => {
db.get(
"SELECT enabled FROM economy_config WHERE guild_id = ?",
[guildId],
(err, row) => {
if (err) return resolve(false);
resolve(!!row?.enabled);
}
);
});
},
slashOptions: [],
executePrefix: async (client, message, args) => {
await doWork(message.guild.id, message.author, (embed) => {
message.reply({ embeds: [embed] });
}, (errMsg) => {
message.reply(errMsg);
});
},
executeSlash: async (client, interaction) => {
await doWork(interaction.guild.id, interaction.user, (embed) => {
interaction.reply({ embeds: [embed] });
}, (errMsg) => {
interaction.reply({ content: errMsg, ephemeral: true });
});
},
});
async function doWork(guildId, user, onSuccess, onError) {
db.get(
"SELECT currency_name, currency_symbol, work_min_amount, work_max_amount, work_cooldown_minutes FROM economy_config WHERE guild_id = ?",
[guildId],
(err, config) => {
if (err || !config) {
return onError("Le système d'économie n'est pas configuré.");
}
db.get(
"SELECT balance, last_work_timestamp FROM user_economy WHERE guild_id = ? AND user_id = ?",
[guildId, user.id],
(err, row) => {
if (err) return onError("Erreur lors de la récupération des données.");
const now = Date.now();
const cooldownMs = config.work_cooldown_minutes * 60 * 1000;
const lastWork = row?.last_work_timestamp || 0;
if (now - lastWork < cooldownMs) {
const timeLeft = cooldownMs - (now - lastWork);
const minutes = Math.floor(timeLeft / (60 * 1000));
const seconds = Math.floor((timeLeft % (60 * 1000)) / 1000);
return onError(`⏰ Vous devez attendre encore **${minutes}m ${seconds}s** avant de pouvoir retravailler.`);
}
const earned = Math.floor(Math.random() * (config.work_max_amount - config.work_min_amount + 1)) + config.work_min_amount;
const newBalance = (row?.balance || 0) + earned;
const workMsg = workMessages[Math.floor(Math.random() * workMessages.length)];
db.run(
`INSERT INTO user_economy (guild_id, user_id, balance, bank, last_work_timestamp)
VALUES (?, ?, ?, 0, ?)
ON CONFLICT(guild_id, user_id) DO UPDATE SET
balance = ?,
last_work_timestamp = ?`,
[guildId, user.id, newBalance, now, newBalance, now],
(err) => {
if (err) return onError("Erreur lors de la sauvegarde.");
const embed = new EmbedBuilder()
.setTitle(`${config.currency_symbol} Travail`)
.setColor("#00FF00")
.setDescription(`${workMsg} **${earned.toLocaleString()} ${config.currency_name}** !`)
.setTimestamp();
onSuccess(embed);
}
);
}
);
}
);
}
+50
View File
@@ -93,6 +93,56 @@ db.exec(`
prefix TEXT NOT NULL DEFAULT '!', prefix TEXT NOT NULL DEFAULT '!',
PRIMARY KEY (guildId) PRIMARY KEY (guildId)
); );
CREATE TABLE IF NOT EXISTS economy_config (
guild_id TEXT PRIMARY KEY,
enabled INTEGER NOT NULL DEFAULT 0,
currency_name TEXT NOT NULL DEFAULT 'coins',
currency_symbol TEXT NOT NULL DEFAULT '💰',
daily_amount INTEGER NOT NULL DEFAULT 100,
daily_cooldown_hours INTEGER NOT NULL DEFAULT 24,
work_min_amount INTEGER NOT NULL DEFAULT 50,
work_max_amount INTEGER NOT NULL DEFAULT 150,
work_cooldown_minutes INTEGER NOT NULL DEFAULT 60,
crime_min_amount INTEGER NOT NULL DEFAULT 100,
crime_max_amount INTEGER NOT NULL DEFAULT 500,
crime_success_rate INTEGER NOT NULL DEFAULT 50,
crime_fine_percent INTEGER NOT NULL DEFAULT 30,
crime_cooldown_minutes INTEGER NOT NULL DEFAULT 120,
starting_balance INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS user_economy (
guild_id TEXT NOT NULL,
user_id TEXT NOT NULL,
balance INTEGER NOT NULL DEFAULT 0,
bank INTEGER NOT NULL DEFAULT 0,
last_daily_timestamp INTEGER,
last_work_timestamp INTEGER,
last_crime_timestamp INTEGER,
PRIMARY KEY (guild_id, user_id)
);
CREATE TABLE IF NOT EXISTS shop_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT,
price INTEGER NOT NULL,
role_id TEXT,
stock INTEGER DEFAULT -1,
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS user_inventory (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id TEXT NOT NULL,
user_id TEXT NOT NULL,
item_id INTEGER NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
purchased_at INTEGER NOT NULL,
FOREIGN KEY (item_id) REFERENCES shop_items(id)
);
`); `);
module.exports = db; module.exports = db;
+22
View File
@@ -24,6 +24,28 @@ module.exports = {
); );
if (!command) return; if (!command) return;
// Vérification du scope / guildCondition
if (command.scope === "guild") {
const guildId = message.guild ? message.guild.id : null;
if (!guildId)
return message
.reply({ content: "Cette commande ne peut pas être utilisée en message privé." })
.then((msg) => setTimeout(() => msg.delete(), 5000));
if (typeof command.guildCondition === "function") {
let allowed = false;
try {
allowed = await command.guildCondition(guildId);
} catch (err) {
console.error(`Erreur guildCondition pour la guild ${guildId}`, err);
allowed = false;
}
if (!allowed)
return message
.reply({ content: "Cette commande est désactivée sur ce serveur." })
.then((msg) => setTimeout(() => msg.delete(), 5000));
}
}
if (command.dm !== true && message.channel.type === 1) if (command.dm !== true && message.channel.type === 1)
return message return message
.reply({ .reply({
+18 -1
View File
@@ -1,4 +1,3 @@
module.exports = { module.exports = {
name: "interactionCreate", name: "interactionCreate",
async execute(client, interaction) { async execute(client, interaction) {
@@ -7,6 +6,24 @@ module.exports = {
const command = client.commands.get(interaction.commandName); const command = client.commands.get(interaction.commandName);
if (!command) return; if (!command) return;
// Vérification du scope / guildCondition
if (command.scope === "guild") {
const guildId = interaction.guild ? interaction.guild.id : null;
if (!guildId)
return interaction.reply({ content: "Cette commande ne peut pas être utilisée en message privé.", ephemeral: true });
if (typeof command.guildCondition === "function") {
let allowed = false;
try {
allowed = await command.guildCondition(guildId);
} catch (err) {
console.error(`Erreur guildCondition pour la guild ${guildId}`, err);
allowed = false;
}
if (!allowed)
return interaction.reply({ content: "Cette commande est désactivée sur ce serveur.", ephemeral: true });
}
}
if (command.dm !== true && interaction.channel.type === 1) if (command.dm !== true && interaction.channel.type === 1)
return interaction.reply({ return interaction.reply({
content: "Cette commande ne peut pas être utilisée en message privé.", content: "Cette commande ne peut pas être utilisée en message privé.",
+16 -8
View File
@@ -43,8 +43,8 @@ function addCommand({
// Permissions // Permissions
let defaultMemberPermissions = null; let defaultMemberPermissions = null;
if (permissions.length > 0) { if (permissions.length > 0) {
defaultMemberPermissions = new PermissionsBitField(); // Résoudre en bitfield (BigInt) compatible avec setDefaultMemberPermissions
permissions.forEach(p => defaultMemberPermissions.add(p)); defaultMemberPermissions = PermissionsBitField.resolve(permissions);
} }
// Création du SlashCommandBuilder // Création du SlashCommandBuilder
@@ -58,14 +58,22 @@ function addCommand({
slashOptions.forEach(opt => { slashOptions.forEach(opt => {
switch (opt.type) { switch (opt.type) {
case "STRING": case "STRING":
slashData.addStringOption(o => slashData.addStringOption(o => {
o.setName(opt.name).setDescription(opt.description || "No description").setRequired(!!opt.required) o.setName(opt.name).setDescription(opt.description || "No description").setRequired(!!opt.required);
); if (opt.choices && Array.isArray(opt.choices)) {
o.addChoices(...opt.choices);
}
return o;
});
break; break;
case "INTEGER": case "INTEGER":
slashData.addIntegerOption(o => slashData.addIntegerOption(o => {
o.setName(opt.name).setDescription(opt.description || "No description").setRequired(!!opt.required) o.setName(opt.name).setDescription(opt.description || "No description").setRequired(!!opt.required);
); if (opt.choices && Array.isArray(opt.choices)) {
o.addChoices(...opt.choices);
}
return o;
});
break; break;
case "BOOLEAN": case "BOOLEAN":
slashData.addBooleanOption(o => slashData.addBooleanOption(o =>
+98
View File
@@ -265,11 +265,109 @@
</form> </form>
<form id="economy-form">
<h2>💰 Système d'économie</h2>
<label>
<input type="checkbox" id="economy-enabled" />
Activer le système d'économie
</label>
<label>
Nom de la monnaie :
<br />
<input type="text" id="economy-currency-name" value="coins" />
</label>
<label>
Symbole de la monnaie :
<br />
<input type="text" id="economy-currency-symbol" value="💰" maxlength="10" />
</label>
<label>
Solde de départ :
<br />
<input type="number" id="economy-starting-balance" min="0" value="0" />
</label>
<h3>📅 Récompense quotidienne</h3>
<label>
Montant quotidien :
<br />
<input type="number" id="economy-daily-amount" min="1" value="100" />
</label>
<label>
Cooldown (heures) :
<br />
<input type="number" id="economy-daily-cooldown" min="1" value="24" />
</label>
<h3>💼 Travail</h3>
<label>
Gain minimum :
<br />
<input type="number" id="economy-work-min" min="1" value="50" />
</label>
<label>
Gain maximum :
<br />
<input type="number" id="economy-work-max" min="1" value="150" />
</label>
<label>
Cooldown (minutes) :
<br />
<input type="number" id="economy-work-cooldown" min="1" value="60" />
</label>
<h3>🔫 Crime</h3>
<label>
Gain minimum :
<br />
<input type="number" id="economy-crime-min" min="1" value="100" />
</label>
<label>
Gain maximum :
<br />
<input type="number" id="economy-crime-max" min="1" value="500" />
</label>
<label>
Taux de réussite (%) :
<br />
<input type="number" id="economy-crime-success" min="1" max="100" value="50" />
</label>
<label>
Amende en cas d'échec (% du solde) :
<br />
<input type="number" id="economy-crime-fine" min="0" max="100" value="30" />
</label>
<label>
Cooldown (minutes) :
<br />
<input type="number" id="economy-crime-cooldown" min="1" value="120" />
</label>
<button type="submit">Sauvegarder</button>
<div id="status-economy-form"></div>
</form>
<script src="/guild/guildBase.js"></script> <script src="/guild/guildBase.js"></script>
<script src="/guild/welcomeForm.js"></script> <script src="/guild/welcomeForm.js"></script>
<script src="/guild/goodbyeForm.js"></script> <script src="/guild/goodbyeForm.js"></script>
<script src="/guild/autoroleNewUserForm.js"></script> <script src="/guild/autoroleNewUserForm.js"></script>
<script src="/guild/autoroleVocalForm.js"></script> <script src="/guild/autoroleVocalForm.js"></script>
<script src="/guild/levelForm.js"></script> <script src="/guild/levelForm.js"></script>
<script src="/guild/economyForm.js"></script>
</body> </body>
</html> </html>
+68
View File
@@ -0,0 +1,68 @@
const economyForm = document.getElementById("economy-form");
const economyEnabled = document.getElementById("economy-enabled");
const currencyName = document.getElementById("economy-currency-name");
const currencySymbol = document.getElementById("economy-currency-symbol");
const startingBalance = document.getElementById("economy-starting-balance");
const dailyAmount = document.getElementById("economy-daily-amount");
const dailyCooldown = document.getElementById("economy-daily-cooldown");
const workMin = document.getElementById("economy-work-min");
const workMax = document.getElementById("economy-work-max");
const workCooldown = document.getElementById("economy-work-cooldown");
const crimeMin = document.getElementById("economy-crime-min");
const crimeMax = document.getElementById("economy-crime-max");
const crimeSuccess = document.getElementById("economy-crime-success");
const crimeFine = document.getElementById("economy-crime-fine");
const crimeCooldown = document.getElementById("economy-crime-cooldown");
const statusEconomyForm = document.getElementById("status-economy-form");
// Charger la config existante
fetch(`/api/bot/get-economy-config/${guildId}`)
.then(res => res.json())
.then(cfg => {
economyEnabled.checked = cfg.enabled;
currencyName.value = cfg.currencyName;
currencySymbol.value = cfg.currencySymbol;
startingBalance.value = cfg.startingBalance;
dailyAmount.value = cfg.dailyAmount;
dailyCooldown.value = cfg.dailyCooldownHours;
workMin.value = cfg.workMinAmount;
workMax.value = cfg.workMaxAmount;
workCooldown.value = cfg.workCooldownMinutes;
crimeMin.value = cfg.crimeMinAmount;
crimeMax.value = cfg.crimeMaxAmount;
crimeSuccess.value = cfg.crimeSuccessRate;
crimeFine.value = cfg.crimeFinePercent;
crimeCooldown.value = cfg.crimeCooldownMinutes;
})
.catch(console.error);
// Sauvegarder la config
economyForm.addEventListener("submit", async e => {
e.preventDefault();
const res = await fetch("/api/bot/save-economy-config", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
guildId,
economyEnabled: economyEnabled.checked,
currencyName: currencyName.value,
currencySymbol: currencySymbol.value,
startingBalance: parseInt(startingBalance.value, 10),
dailyAmount: parseInt(dailyAmount.value, 10),
dailyCooldownHours: parseInt(dailyCooldown.value, 10),
workMinAmount: parseInt(workMin.value, 10),
workMaxAmount: parseInt(workMax.value, 10),
workCooldownMinutes: parseInt(workCooldown.value, 10),
crimeMinAmount: parseInt(crimeMin.value, 10),
crimeMaxAmount: parseInt(crimeMax.value, 10),
crimeSuccessRate: parseInt(crimeSuccess.value, 10),
crimeFinePercent: parseInt(crimeFine.value, 10),
crimeCooldownMinutes: parseInt(crimeCooldown.value, 10)
})
});
statusEconomyForm.textContent = (await res.json()).success
? "Config économie sauvegardée ✅"
: "Erreur ❌";
});
+117
View File
@@ -548,5 +548,122 @@ module.exports = (app, db, client) => {
); );
}); });
// ============== ECONOMY CONFIG ==============
router.get("/bot/get-economy-config/:guildId", (req, res) => {
const { guildId } = req.params;
db.get(
`SELECT * FROM economy_config WHERE guild_id = ?`,
[guildId],
(err, row) => {
if (err || !row) {
return res.json({
enabled: false,
currencyName: "coins",
currencySymbol: "💰",
dailyAmount: 100,
dailyCooldownHours: 24,
workMinAmount: 50,
workMaxAmount: 150,
workCooldownMinutes: 60,
crimeMinAmount: 100,
crimeMaxAmount: 500,
crimeSuccessRate: 50,
crimeFinePercent: 30,
crimeCooldownMinutes: 120,
startingBalance: 0
});
}
res.json({
enabled: !!row.enabled,
currencyName: row.currency_name,
currencySymbol: row.currency_symbol,
dailyAmount: row.daily_amount,
dailyCooldownHours: row.daily_cooldown_hours,
workMinAmount: row.work_min_amount,
workMaxAmount: row.work_max_amount,
workCooldownMinutes: row.work_cooldown_minutes,
crimeMinAmount: row.crime_min_amount,
crimeMaxAmount: row.crime_max_amount,
crimeSuccessRate: row.crime_success_rate,
crimeFinePercent: row.crime_fine_percent,
crimeCooldownMinutes: row.crime_cooldown_minutes,
startingBalance: row.starting_balance
});
}
);
});
router.post("/bot/save-economy-config", express.json(), (req, res) => {
const {
guildId,
economyEnabled,
currencyName,
currencySymbol,
dailyAmount,
dailyCooldownHours,
workMinAmount,
workMaxAmount,
workCooldownMinutes,
crimeMinAmount,
crimeMaxAmount,
crimeSuccessRate,
crimeFinePercent,
crimeCooldownMinutes,
startingBalance
} = req.body;
if (!req.session.guilds) {
return res.status(401).json({ success: false });
}
const isAdmin = req.session.guilds.find(
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
);
if (!isAdmin) {
return res.status(403).json({ success: false });
}
db.run(
`INSERT INTO economy_config (
guild_id, enabled, currency_name, currency_symbol,
daily_amount, daily_cooldown_hours,
work_min_amount, work_max_amount, work_cooldown_minutes,
crime_min_amount, crime_max_amount, crime_success_rate, crime_fine_percent, crime_cooldown_minutes,
starting_balance
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(guild_id) DO UPDATE SET
enabled = ?, currency_name = ?, currency_symbol = ?,
daily_amount = ?, daily_cooldown_hours = ?,
work_min_amount = ?, work_max_amount = ?, work_cooldown_minutes = ?,
crime_min_amount = ?, crime_max_amount = ?, crime_success_rate = ?, crime_fine_percent = ?, crime_cooldown_minutes = ?,
starting_balance = ?`,
[
guildId,
economyEnabled ? 1 : 0, currencyName, currencySymbol,
dailyAmount, dailyCooldownHours,
workMinAmount, workMaxAmount, workCooldownMinutes,
crimeMinAmount, crimeMaxAmount, crimeSuccessRate, crimeFinePercent, crimeCooldownMinutes,
startingBalance,
economyEnabled ? 1 : 0, currencyName, currencySymbol,
dailyAmount, dailyCooldownHours,
workMinAmount, workMaxAmount, workCooldownMinutes,
crimeMinAmount, crimeMaxAmount, crimeSuccessRate, crimeFinePercent, crimeCooldownMinutes,
startingBalance
],
err => {
if (err) {
console.error(err);
return res.status(500).json({ success: false });
}
loadSlashCommands(client, guildId);
res.json({ success: true });
}
);
});
app.use("/api", router); app.use("/api", router);
}; };