mirror of
https://github.com/arthur-pbty/LazyBot.git
synced 2026-06-03 23:36:37 +02:00
add economi system
This commit is contained in:
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -93,6 +93,56 @@ db.exec(`
|
||||
prefix TEXT NOT NULL DEFAULT '!',
|
||||
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;
|
||||
|
||||
@@ -24,6 +24,28 @@ module.exports = {
|
||||
);
|
||||
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)
|
||||
return message
|
||||
.reply({
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
module.exports = {
|
||||
name: "interactionCreate",
|
||||
async execute(client, interaction) {
|
||||
@@ -7,6 +6,24 @@ module.exports = {
|
||||
const command = client.commands.get(interaction.commandName);
|
||||
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)
|
||||
return interaction.reply({
|
||||
content: "Cette commande ne peut pas être utilisée en message privé.",
|
||||
|
||||
@@ -43,8 +43,8 @@ function addCommand({
|
||||
// Permissions
|
||||
let defaultMemberPermissions = null;
|
||||
if (permissions.length > 0) {
|
||||
defaultMemberPermissions = new PermissionsBitField();
|
||||
permissions.forEach(p => defaultMemberPermissions.add(p));
|
||||
// Résoudre en bitfield (BigInt) compatible avec setDefaultMemberPermissions
|
||||
defaultMemberPermissions = PermissionsBitField.resolve(permissions);
|
||||
}
|
||||
|
||||
// Création du SlashCommandBuilder
|
||||
@@ -58,14 +58,22 @@ function addCommand({
|
||||
slashOptions.forEach(opt => {
|
||||
switch (opt.type) {
|
||||
case "STRING":
|
||||
slashData.addStringOption(o =>
|
||||
o.setName(opt.name).setDescription(opt.description || "No description").setRequired(!!opt.required)
|
||||
);
|
||||
slashData.addStringOption(o => {
|
||||
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;
|
||||
case "INTEGER":
|
||||
slashData.addIntegerOption(o =>
|
||||
o.setName(opt.name).setDescription(opt.description || "No description").setRequired(!!opt.required)
|
||||
);
|
||||
slashData.addIntegerOption(o => {
|
||||
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;
|
||||
case "BOOLEAN":
|
||||
slashData.addBooleanOption(o =>
|
||||
|
||||
@@ -263,6 +263,103 @@
|
||||
<button type="submit">Sauvegarder</button>
|
||||
<div id="status-level-form"></div>
|
||||
</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>
|
||||
@@ -271,5 +368,6 @@
|
||||
<script src="/guild/autoroleNewUserForm.js"></script>
|
||||
<script src="/guild/autoroleVocalForm.js"></script>
|
||||
<script src="/guild/levelForm.js"></script>
|
||||
<script src="/guild/economyForm.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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 ❌";
|
||||
});
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user