Push structure

This commit is contained in:
Loïc 2025-10-21 02:13:56 +02:00
parent da04474185
commit 7d6bc8e12f
15 changed files with 992 additions and 2 deletions

14
.env Normal file
View File

@ -0,0 +1,14 @@
TOKEN=ODcwNzA3OTcyMjE1NTUwMDE0.GNCXuj.kPaJfU-QHKZk7uBvzblOmJHSv9QllNRIIp5dGg
CLIENT_ID=870707972215550014
OWNER_ID=574544938440851466
SUPPORT_SERVER_ID=840770323988873227
CHANNEL_REQUEST_ACCESS_ID=1402029493258031114
CHANNEL_ACCESS_STAFF_REQUESTS_ID=1429968603905786000
ROLE_WAITER_ID=1429968069450530987
ROLE_VERIFIED_ID=1429968133304615012
ROLE_ADMIN_ID=1164572260607205486
MINECRAFT_SERVER_IP=valloic.dev:25565
MINECRAFT_WHITELIST_PATH=/opt/minecraft/server/whitelist.json

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

134
README.md
View File

@ -1,3 +1,133 @@
# mc-acces-bot
# Bot Discord - Accès Serveur Minecraft
Bot privé pour gérer mon serveur Minecraft.
Ce bot Discord permet de gérer automatiquement les demandes d'accès à un serveur Minecraft via un système de whitelist.
## Fonctionnalités
### 🎮 Système de demande d'accès
- Message automatique avec bouton pour demander l'accès
- Modal pour saisir le pseudo Minecraft
- Vérification automatique si le joueur est déjà whitelisté
- Envoi de la demande au canal staff avec boutons Accept/Refuse
### ⚙️ Commandes administrateur
- `/minecraft list` - Affiche la liste des joueurs whitelistés
- `/minecraft add <pseudo>` - Ajoute un joueur à la whitelist
- `/minecraft remove <pseudo>` - Retire un joueur de la whitelist
- `/minecraft status` - Affiche le statut du serveur Minecraft
- `/setup-access` - Envoie le message de demande d'accès
## Installation
1. **Cloner le projet et installer les dépendances :**
```bash
npm install
```
2. **Configurer le fichier .env :**
Assurez-vous que votre fichier `.env` contient toutes les variables nécessaires :
```env
TOKEN=votre_token_bot_discord
CLIENT_ID=id_du_bot
OWNER_ID=votre_id_discord
SUPPORT_SERVER_ID=id_du_serveur_discord
CHANNEL_REQUEST_ACCESS_ID=id_canal_demandes_publiques
CHANNEL_ACCESS_STAFF_REQUESTS_ID=id_canal_staff_demandes
ROLE_WAITER_ID=id_role_attente
ROLE_VERIFIED_ID=id_role_verifie
ROLE_ADMIN_ID=id_role_admin
MINECRAFT_SERVER_IP=adresse:port
MINECRAFT_WHITELIST_PATH=/chemin/vers/whitelist.json
```
3. **Lancer le bot :**
```bash
npm start
```
Pour le développement :
```bash
npm run dev
```
## Structure du projet
```
├── start.js # Point d'entrée principal
├── src/
│ ├── commands/
│ │ ├── minecraft.js # Commandes /minecraft
│ │ └── setup-access.js # Commande /setup-access
│ ├── events/
│ │ └── ready.js # Événement de démarrage
│ ├── handlers/
│ │ ├── commandHandler.js # Chargeur de commandes
│ │ ├── eventHandler.js # Chargeur d'événements
│ │ └── interactionHandler.js # Gestionnaire d'interactions
│ └── utils/
│ ├── accessRequestManager.js # Gestionnaire de demandes d'accès
│ ├── minecraftServer.js # Utilitaires serveur Minecraft
│ └── whitelistManager.js # Gestionnaire de whitelist
```
## Utilisation
### Configuration initiale
1. **Inviter le bot sur votre serveur** avec les permissions nécessaires
2. **Utiliser `/setup-access`** dans le canal où vous voulez le message de demande d'accès
3. **Configurer les rôles** dans votre serveur Discord selon les IDs dans le `.env`
### Workflow des demandes
1. **Utilisateur :** Clique sur le bouton "Demander l'accès"
2. **Utilisateur :** Remplit le modal avec son pseudo Minecraft
3. **Système :** Vérifie si le joueur est déjà whitelisté
4. **Système :** Envoie la demande au canal staff
5. **Staff :** Clique sur "Accepter" ou "Refuser"
6. **Système :** Ajoute le joueur à la whitelist (si accepté) et notifie l'utilisateur
### Commandes administrateur
Seul l'OWNER_ID peut utiliser les commandes `/minecraft` :
- **Lister les joueurs :** `/minecraft list`
- **Ajouter un joueur :** `/minecraft add pseudo:NomDuJoueur`
- **Retirer un joueur :** `/minecraft remove pseudo:NomDuJoueur`
- **Status du serveur :** `/minecraft status`
## Permissions requises
Le bot a besoin des permissions Discord suivantes :
- `Send Messages` - Envoyer des messages
- `Use Slash Commands` - Utiliser les commandes slash
- `Embed Links` - Incorporer des liens
- `Manage Messages` - Gérer les messages
- `Read Message History` - Lire l'historique des messages
## Notes importantes
- Le fichier `whitelist.json` doit être accessible en lecture/écriture
- Le serveur Minecraft doit être accessible pour la vérification du statut
- Les IDs Discord dans le `.env` doivent être valides
- Le bot vérifie automatiquement les pseudos via l'API Mojang
## Dépannage
### Le bot ne répond pas aux commandes
- Vérifiez que le TOKEN est correct
- Vérifiez que les permissions sont accordées
- Consultez les logs dans la console
### Erreurs de whitelist
- Vérifiez le chemin vers `whitelist.json`
- Vérifiez les permissions de fichier
- Assurez-vous que le format JSON est valide
### Problèmes de serveur Minecraft
- Vérifiez que l'adresse IP et le port sont corrects
- Assurez-vous que le serveur est en ligne
- Vérifiez les paramètres du pare-feu

9
emojis.json Normal file
View File

@ -0,0 +1,9 @@
{
"added_whitelist_emoji": "<:securityenable:1429980226351534090>",
"removed_whitelist_emoji": "<:securitydisable:1429980224984055960>",
"refused_whitelist_emoji": "<:securitydisable:1429980224984055960>",
"pending_whitelist_emoji": "<:pending:1429980711481245768>",
"error_emoji": "<:error:1429980784076521614>",
"info_emoji": "<:info:1429981238130774016>",
"success_emoji": "<a:success:1429980863663312928>"
}

27
package.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "mc-access-bot",
"version": "1.0.0",
"description": "Bot Discord pour gérer l'accès au serveur Minecraft",
"main": "start.js",
"scripts": {
"start": "node start.js",
"dev": "nodemon start.js"
},
"keywords": [
"discord",
"minecraft",
"bot",
"whitelist"
],
"author": "",
"license": "MIT",
"dependencies": {
"discord.js": "^14.14.1",
"dotenv": "^16.3.1",
"minecraft-server-util": "^5.4.3",
"node-fetch": "^3.3.2"
},
"devDependencies": {
"nodemon": "^3.0.2"
}
}

126
src/commands/minecraft.js Normal file
View File

@ -0,0 +1,126 @@
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
const WhitelistManager = require('../utils/whitelistManager');
const MinecraftServerUtil = require('../utils/minecraftServer');
module.exports = {
data: new SlashCommandBuilder()
.setName('minecraft')
.setDescription('Commandes de gestion du serveur Minecraft')
.addSubcommand(subcommand =>
subcommand
.setName('list')
.setDescription('Affiche la liste des joueurs whitelistés')
)
.addSubcommand(subcommand =>
subcommand
.setName('add')
.setDescription('Ajoute un joueur à la whitelist')
.addStringOption(option =>
option
.setName('pseudo')
.setDescription('Le pseudo Minecraft du joueur à ajouter')
.setRequired(true)
)
)
.addSubcommand(subcommand =>
subcommand
.setName('remove')
.setDescription('Retire un joueur de la whitelist')
.addStringOption(option =>
option
.setName('pseudo')
.setDescription('Le pseudo Minecraft du joueur à retirer')
.setRequired(true)
)
)
.addSubcommand(subcommand =>
subcommand
.setName('status')
.setDescription('Affiche le status du serveur Minecraft')
),
async execute(interaction) {
// Vérifier si l'utilisateur est le propriétaire
if (interaction.user.id !== process.env.OWNER_ID) {
return await interaction.reply({
content: '❌ Vous n\'avez pas la permission d\'utiliser cette commande.',
ephemeral: true
});
}
const subcommand = interaction.options.getSubcommand();
const whitelistManager = new WhitelistManager();
const serverUtil = new MinecraftServerUtil();
await interaction.deferReply();
try {
switch (subcommand) {
case 'list': {
const players = await whitelistManager.getWhitelistPlayers();
const embed = new EmbedBuilder()
.setTitle('📋 Liste des joueurs whitelistés')
.setColor('#0099FF')
.setTimestamp();
if (players.length === 0) {
embed.setDescription('Aucun joueur dans la whitelist.');
} else {
const playerList = players.join('\n');
embed.setDescription(`**${players.length} joueur(s) whitelisté(s) :**\n\`\`\`\n${playerList}\n\`\`\``);
}
await interaction.editReply({ embeds: [embed] });
break;
}
case 'add': {
const pseudo = interaction.options.getString('pseudo');
const result = await whitelistManager.addPlayer(pseudo);
const embed = new EmbedBuilder()
.setTitle(result.success ? '✅ Joueur ajouté' : '❌ Erreur')
.setDescription(result.message)
.setColor(result.success ? '#00FF00' : '#FF0000')
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
break;
}
case 'remove': {
const pseudo = interaction.options.getString('pseudo');
const result = await whitelistManager.removePlayer(pseudo);
const embed = new EmbedBuilder()
.setTitle(result.success ? '✅ Joueur retiré' : '❌ Erreur')
.setDescription(result.message)
.setColor(result.success ? '#00FF00' : '#FF0000')
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
break;
}
case 'status': {
const statusData = await serverUtil.getServerStatus();
const embed = serverUtil.formatStatusEmbed(statusData);
await interaction.editReply({ embeds: [embed] });
break;
}
}
} catch (error) {
console.error('Erreur dans la commande minecraft:', error);
const errorEmbed = new EmbedBuilder()
.setTitle('❌ Erreur')
.setDescription('Une erreur est survenue lors de l\'exécution de la commande.')
.setColor('#FF0000')
.setTimestamp();
await interaction.editReply({ embeds: [errorEmbed] });
}
}
};

View File

@ -0,0 +1,40 @@
const { SlashCommandBuilder } = require('discord.js');
const AccessRequestManager = require('../utils/accessRequestManager');
module.exports = {
data: new SlashCommandBuilder()
.setName('setup-access')
.setDescription('Envoie le message de demande d\'accès dans le canal actuel'),
async execute(interaction) {
// Vérifier si l'utilisateur est le propriétaire
if (interaction.user.id !== process.env.OWNER_ID) {
return await interaction.reply({
content: '❌ Vous n\'avez pas la permission d\'utiliser cette commande.',
ephemeral: true
});
}
try {
const embed = AccessRequestManager.createRequestEmbed();
const button = AccessRequestManager.createRequestButton();
await interaction.reply({
content: '✅ Message de demande d\'accès envoyé !',
ephemeral: true
});
await interaction.followUp({
embeds: [embed],
components: [button]
});
} catch (error) {
console.error('Erreur lors de l\'envoi du message de demande d\'accès:', error);
await interaction.reply({
content: '❌ Erreur lors de l\'envoi du message.',
ephemeral: true
});
}
}
};

47
src/events/ready.js Normal file
View File

@ -0,0 +1,47 @@
const { REST, Routes } = require('discord.js');
module.exports = {
name: 'ready',
once: true,
async execute(client) {
console.log(`🚀 ${client.user.tag} est maintenant en ligne !`);
console.log(`📊 Connecté à ${client.guilds.cache.size} serveur(s)`);
console.log(`👥 ${client.users.cache.size} utilisateur(s) dans le cache`);
// Enregistrer les commandes slash
try {
console.log('🔄 Début de l\'actualisation des commandes slash...');
const commands = [];
for (const command of client.commands.values()) {
commands.push(command.data.toJSON());
}
const rest = new REST().setToken(process.env.TOKEN);
// Pour un serveur spécifique (plus rapide pendant le développement)
if (process.env.SUPPORT_SERVER_ID) {
await rest.put(
Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.SUPPORT_SERVER_ID),
{ body: commands }
);
console.log(`${commands.length} commande(s) slash enregistrée(s) pour le serveur de support.`);
}
// Pour tous les serveurs (global) - décommentez si nécessaire
/*
await rest.put(
Routes.applicationCommands(process.env.CLIENT_ID),
{ body: commands }
);
console.log(`${commands.length} commande(s) slash enregistrée(s) globalement.`);
*/
} catch (error) {
console.error('❌ Erreur lors de l\'enregistrement des commandes slash:', error);
}
// Définir le statut du bot
client.user.setActivity(`${process.env.MINECRAFT_SERVER_IP}`, { type: 'WATCHING' });
},
};

View File

@ -0,0 +1,26 @@
const fs = require('fs');
const path = require('path');
const { Collection } = require('discord.js');
async function loadCommands(client) {
client.commands = new Collection();
const commandsPath = path.join(__dirname, '../commands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
client.commands.set(command.data.name, command);
console.log(`✅ Commande chargée: ${command.data.name}`);
} else {
console.log(`⚠️ La commande ${filePath} n'a pas les propriétés "data" ou "execute" requises.`);
}
}
console.log(`📝 ${commandFiles.length} commande(s) chargée(s).`);
}
module.exports = { loadCommands };

View File

@ -0,0 +1,35 @@
const fs = require('fs');
const path = require('path');
async function loadEvents(client) {
const eventsPath = path.join(__dirname, '../events');
// Créer le dossier events s'il n'existe pas
if (!fs.existsSync(eventsPath)) {
fs.mkdirSync(eventsPath, { recursive: true });
}
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
for (const file of eventFiles) {
const filePath = path.join(eventsPath, file);
const event = require(filePath);
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
} else {
client.on(event.name, (...args) => event.execute(...args));
}
console.log(`✅ Événement chargé: ${event.name}`);
}
// Charger directement l'handler d'interactions
const interactionHandler = require('./interactionHandler');
client.on('interactionCreate', (...args) => interactionHandler.execute(...args));
console.log('✅ Handler d\'interactions chargé');
console.log(`📝 ${eventFiles.length + 1} événement(s) chargé(s).`);
}
module.exports = { loadEvents };

View File

@ -0,0 +1,191 @@
const { ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } = require('discord.js');
const AccessRequestManager = require('../utils/accessRequestManager');
const WhitelistManager = require('../utils/whitelistManager');
module.exports = {
name: 'interactionCreate',
async execute(interaction) {
if (interaction.isChatInputCommand()) {
const command = interaction.client.commands.get(interaction.commandName);
if (!command) {
console.error(`Aucune commande correspondant à ${interaction.commandName} n'a été trouvée.`);
return;
}
try {
await command.execute(interaction);
} catch (error) {
console.error('Erreur lors de l\'exécution de la commande:', error);
const reply = {
content: 'Il y a eu une erreur lors de l\'exécution de cette commande !',
ephemeral: true
};
if (interaction.replied || interaction.deferred) {
await interaction.followUp(reply);
} else {
await interaction.reply(reply);
}
}
} else if (interaction.isButton()) {
await handleButtonInteraction(interaction);
} else if (interaction.isModalSubmit()) {
await handleModalSubmit(interaction);
}
},
};
async function handleButtonInteraction(interaction) {
if (interaction.customId === 'request_access') {
// Créer le modal pour demander le pseudo Minecraft
const modal = new ModalBuilder()
.setCustomId('minecraft_access_modal')
.setTitle('Demande d\'accès Minecraft');
const usernameInput = new TextInputBuilder()
.setCustomId('minecraft_username')
.setLabel('Votre pseudo Minecraft')
.setStyle(TextInputStyle.Short)
.setPlaceholder('Entrez votre pseudo Minecraft exact...')
.setRequired(true)
.setMaxLength(16)
.setMinLength(3);
const firstActionRow = new ActionRowBuilder().addComponents(usernameInput);
modal.addComponents(firstActionRow);
await interaction.showModal(modal);
} else if (interaction.customId.startsWith('accept_request_') || interaction.customId.startsWith('deny_request_')) {
await handleStaffResponse(interaction);
}
}
async function handleModalSubmit(interaction) {
if (interaction.customId === 'minecraft_access_modal') {
const username = interaction.fields.getTextInputValue('minecraft_username');
await interaction.deferReply({ ephemeral: true });
try {
// Vérifier si le joueur est déjà whitelisté
const whitelistManager = new WhitelistManager();
const isAlreadyWhitelisted = await whitelistManager.isPlayerWhitelisted(username);
// Envoyer la demande au canal du staff
const staffChannel = interaction.client.channels.cache.get(process.env.CHANNEL_ACCESS_STAFF_REQUESTS_ID);
if (!staffChannel) {
return await interaction.editReply({
content: '❌ Erreur : Canal du staff introuvable. Contactez un administrateur.'
});
}
const embed = AccessRequestManager.createStaffEmbed(interaction.user, username, isAlreadyWhitelisted);
const buttons = AccessRequestManager.createStaffButtons(interaction.user.id, username, isAlreadyWhitelisted);
await staffChannel.send({
embeds: [embed],
components: [buttons]
});
// Répondre à l'utilisateur
let responseMessage = '✅ Votre demande d\'accès a été envoyée à l\'équipe administrative !';
if (isAlreadyWhitelisted) {
responseMessage += '\n\n⚠ **Note :** Vous êtes déjà dans la whitelist du serveur.';
}
await interaction.editReply({
content: responseMessage
});
} catch (error) {
console.error('Erreur lors du traitement de la demande d\'accès:', error);
await interaction.editReply({
content: '❌ Une erreur est survenue lors du traitement de votre demande. Veuillez réessayer.'
});
}
}
}
async function handleStaffResponse(interaction) {
// Vérifier si l'utilisateur a le rôle admin
if (!interaction.member.roles.cache.has(process.env.ROLE_ADMIN_ID)) {
return await interaction.reply({
content: '❌ Vous n\'avez pas la permission de traiter les demandes d\'accès.',
ephemeral: true
});
}
const [action, , userId, username] = interaction.customId.split('_');
const isAccepted = action === 'accept';
await interaction.deferReply({ ephemeral: true });
try {
const whitelistManager = new WhitelistManager();
let result = { success: true, message: '' };
if (isAccepted) {
// Ajouter le joueur à la whitelist
result = await whitelistManager.addPlayer(username);
}
// Envoyer une réponse à l'utilisateur qui a fait la demande
try {
const user = await interaction.client.users.fetch(userId);
const responseEmbed = AccessRequestManager.createResponseEmbed(
isAccepted && result.success,
username,
isAccepted && !result.success ? result.message : null
);
await user.send({ embeds: [responseEmbed] });
} catch (error) {
console.error('Impossible d\'envoyer un MP à l\'utilisateur:', error);
}
// Mettre à jour le message original
const originalEmbed = interaction.message.embeds[0];
const updatedEmbed = {
...originalEmbed.toJSON(),
color: isAccepted && result.success ? 0x00FF00 : 0xFF0000,
fields: [
...originalEmbed.fields,
{
name: isAccepted ? '✅ Accepté par' : '❌ Refusé par',
value: `${interaction.user.tag} (<t:${Math.floor(Date.now() / 1000)}:R>)`,
inline: false
}
]
};
if (isAccepted && !result.success) {
updatedEmbed.fields.push({
name: '⚠️ Erreur',
value: result.message,
inline: false
});
}
await interaction.message.edit({
embeds: [updatedEmbed],
components: [] // Retirer les boutons
});
// Répondre au staff member
const statusMessage = isAccepted
? (result.success ? `✅ Demande acceptée et ${username} ajouté à la whitelist.` : `❌ Erreur lors de l'ajout : ${result.message}`)
: `❌ Demande refusée pour ${username}.`;
await interaction.editReply({
content: statusMessage
});
} catch (error) {
console.error('Erreur lors du traitement de la réponse du staff:', error);
await interaction.editReply({
content: '❌ Une erreur est survenue lors du traitement de la demande.'
});
}
}

View File

@ -0,0 +1,110 @@
const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
class AccessRequestManager {
static createRequestEmbed() {
return new EmbedBuilder()
.setTitle('🎮 Accès au serveur Minecraft')
.setDescription(
'Bienvenue ! Pour rejoindre notre serveur Minecraft, vous devez être ajouté à la whitelist.\n\n' +
'**Comment procéder :**\n' +
'1⃣ Cliquez sur le bouton "Demander l\'accès" ci-dessous\n' +
'2⃣ Entrez votre pseudo Minecraft exact\n' +
'3⃣ Attendez l\'approbation d\'un administrateur\n\n' +
'**Informations importantes :**\n' +
'• Votre pseudo doit être valide et correspondre à votre compte Minecraft\n' +
'• Si vous êtes déjà whitelisté, le système vous en informera\n' +
'• Les demandes sont traitées par l\'équipe administrative\n\n' +
`**Adresse du serveur :** \`${process.env.MINECRAFT_SERVER_IP}\``
)
.setColor('#00FF00')
.setThumbnail('https://i.imgur.com/YSO5z0H.png') // Image Minecraft
.setFooter({ text: 'Serveur Minecraft • Demande d\'accès' })
.setTimestamp();
}
static createRequestButton() {
return new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId('request_access')
.setLabel('Demander l\'accès')
.setStyle(ButtonStyle.Primary)
.setEmoji('🎮')
);
}
static createStaffEmbed(user, username, isAlreadyWhitelisted = false) {
const embed = new EmbedBuilder()
.setTitle('📋 Nouvelle demande d\'accès Minecraft')
.setDescription(`${user} souhaite rejoindre le serveur Minecraft.`)
.addFields(
{ name: '👤 Utilisateur Discord', value: `${user.tag} (${user.id})`, inline: true },
{ name: '🎮 Pseudo Minecraft', value: `\`${username}\``, inline: true },
{ name: '📅 Date de la demande', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: false }
)
.setThumbnail(user.displayAvatarURL({ dynamic: true }))
.setTimestamp();
if (isAlreadyWhitelisted) {
embed.setColor('#FFA500')
.addFields({ name: '⚠️ Avertissement', value: 'Ce joueur est déjà dans la whitelist !', inline: false });
} else {
embed.setColor('#0099FF');
}
return embed;
}
static createStaffButtons(userId, username, isAlreadyWhitelisted = false) {
const row = new ActionRowBuilder();
if (!isAlreadyWhitelisted) {
row.addComponents(
new ButtonBuilder()
.setCustomId(`accept_request_${userId}_${username}`)
.setLabel('Accepter')
.setStyle(ButtonStyle.Success)
.setEmoji('✅')
);
}
row.addComponents(
new ButtonBuilder()
.setCustomId(`deny_request_${userId}_${username}`)
.setLabel('Refuser')
.setStyle(ButtonStyle.Danger)
.setEmoji('❌')
);
return row;
}
static createResponseEmbed(accepted, username, reason = null) {
const embed = new EmbedBuilder()
.setTimestamp();
if (accepted) {
embed.setTitle('✅ Demande acceptée !')
.setDescription(
`Félicitations ! Votre demande d'accès au serveur Minecraft a été **acceptée**.\n\n` +
`**Pseudo ajouté :** \`${username}\`\n` +
`**Adresse du serveur :** \`${process.env.MINECRAFT_SERVER_IP}\`\n\n` +
`Vous pouvez maintenant vous connecter au serveur !`
)
.setColor('#00FF00');
} else {
embed.setTitle('❌ Demande refusée')
.setDescription(
`Votre demande d'accès au serveur Minecraft a été **refusée**.\n\n` +
`**Pseudo demandé :** \`${username}\`\n` +
(reason ? `**Raison :** ${reason}\n\n` : '') +
`Vous pouvez faire une nouvelle demande si nécessaire.`
)
.setColor('#FF0000');
}
return embed;
}
}
module.exports = AccessRequestManager;

View File

@ -0,0 +1,82 @@
const { status } = require('minecraft-server-util');
class MinecraftServerUtil {
constructor() {
this.serverIP = process.env.MINECRAFT_SERVER_IP;
this.host = this.serverIP.split(':')[0];
this.port = parseInt(this.serverIP.split(':')[1]) || 25565;
}
async getServerStatus() {
try {
const result = await status(this.host, this.port);
return {
online: true,
version: result.version.name,
protocol: result.version.protocol,
players: {
online: result.players.online,
max: result.players.max,
list: result.players.sample || []
},
description: result.description.text || result.description,
latency: result.roundTripLatency,
favicon: result.favicon
};
} catch (error) {
console.error('Erreur lors de la vérification du serveur:', error);
return {
online: false,
error: error.message
};
}
}
async isServerOnline() {
const status = await this.getServerStatus();
return status.online;
}
formatStatusEmbed(statusData) {
const { EmbedBuilder } = require('discord.js');
if (!statusData.online) {
return new EmbedBuilder()
.setTitle('🔴 Serveur Minecraft - Hors ligne')
.setDescription(`Le serveur \`${this.serverIP}\` est actuellement hors ligne.`)
.addFields(
{ name: 'Erreur', value: statusData.error || 'Serveur inaccessible', inline: true }
)
.setColor('#FF0000')
.setTimestamp();
}
const embed = new EmbedBuilder()
.setTitle('🟢 Serveur Minecraft - En ligne')
.setDescription(`Le serveur \`${this.serverIP}\` est en ligne !`)
.addFields(
{ name: '👥 Joueurs', value: `${statusData.players.online}/${statusData.players.max}`, inline: true },
{ name: '📋 Version', value: statusData.version, inline: true },
{ name: '📡 Latence', value: `${statusData.latency}ms`, inline: true }
)
.setColor('#00FF00')
.setTimestamp();
if (statusData.description) {
embed.addFields({ name: '📝 Description', value: statusData.description, inline: false });
}
if (statusData.players.list && statusData.players.list.length > 0) {
const playerList = statusData.players.list
.slice(0, 10) // Limiter à 10 joueurs
.map(player => player.name)
.join(', ');
embed.addFields({ name: '🎮 Joueurs connectés', value: playerList, inline: false });
}
return embed;
}
}
module.exports = MinecraftServerUtil;

View File

@ -0,0 +1,119 @@
const fs = require('fs').promises;
const path = require('path');
class WhitelistManager {
constructor() {
this.whitelistPath = process.env.MINECRAFT_WHITELIST_PATH;
}
async readWhitelist() {
try {
const data = await fs.readFile(this.whitelistPath, 'utf8');
return JSON.parse(data);
} catch (error) {
console.error('Erreur lors de la lecture de la whitelist:', error);
return [];
}
}
async writeWhitelist(whitelist) {
try {
await fs.writeFile(this.whitelistPath, JSON.stringify(whitelist, null, 2));
return true;
} catch (error) {
console.error('Erreur lors de l\'écriture de la whitelist:', error);
return false;
}
}
async isPlayerWhitelisted(username) {
const whitelist = await this.readWhitelist();
return whitelist.some(player =>
player.name.toLowerCase() === username.toLowerCase()
);
}
async addPlayer(username, uuid = null) {
try {
const whitelist = await this.readWhitelist();
// Vérifier si le joueur existe déjà
if (await this.isPlayerWhitelisted(username)) {
return { success: false, message: 'Le joueur est déjà dans la whitelist.' };
}
// Si pas d'UUID fourni, essayer de le récupérer via l'API Mojang
if (!uuid) {
uuid = await this.getUUIDFromUsername(username);
if (!uuid) {
return { success: false, message: 'Impossible de trouver l\'UUID pour ce joueur.' };
}
}
// Ajouter le joueur
whitelist.push({
uuid: uuid,
name: username
});
const success = await this.writeWhitelist(whitelist);
if (success) {
return { success: true, message: `${username} a été ajouté à la whitelist.` };
} else {
return { success: false, message: 'Erreur lors de l\'ajout du joueur.' };
}
} catch (error) {
console.error('Erreur lors de l\'ajout du joueur:', error);
return { success: false, message: 'Erreur interne lors de l\'ajout du joueur.' };
}
}
async removePlayer(username) {
try {
const whitelist = await this.readWhitelist();
const initialLength = whitelist.length;
const newWhitelist = whitelist.filter(player =>
player.name.toLowerCase() !== username.toLowerCase()
);
if (newWhitelist.length === initialLength) {
return { success: false, message: 'Le joueur n\'est pas dans la whitelist.' };
}
const success = await this.writeWhitelist(newWhitelist);
if (success) {
return { success: true, message: `${username} a été retiré de la whitelist.` };
} else {
return { success: false, message: 'Erreur lors de la suppression du joueur.' };
}
} catch (error) {
console.error('Erreur lors de la suppression du joueur:', error);
return { success: false, message: 'Erreur interne lors de la suppression du joueur.' };
}
}
async getWhitelistPlayers() {
const whitelist = await this.readWhitelist();
return whitelist.map(player => player.name);
}
async getUUIDFromUsername(username) {
try {
const fetch = (await import('node-fetch')).default;
const response = await fetch(`https://api.mojang.com/users/profiles/minecraft/${username}`);
if (!response.ok) {
return null;
}
const data = await response.json();
return data.id;
} catch (error) {
console.error('Erreur lors de la récupération de l\'UUID:', error);
return null;
}
}
}
module.exports = WhitelistManager;

33
start.js Normal file
View File

@ -0,0 +1,33 @@
require('dotenv').config();
const { Client, GatewayIntentBits } = require('discord.js');
const { loadCommands } = require('./src/handlers/commandHandler');
const { loadEvents } = require('./src/handlers/eventHandler');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildMembers
]
});
async function startBot() {
try {
console.log('🚀 Démarrage du bot...');
// Charger les commandes et événements
await loadCommands(client);
await loadEvents(client);
// Connexion du bot
await client.login(process.env.TOKEN);
console.log('✅ Bot connecté avec succès !');
} catch (error) {
console.error('❌ Erreur lors du démarrage du bot:', error);
process.exit(1);
}
}
startBot();