Push structure
This commit is contained in:
parent
da04474185
commit
7d6bc8e12f
14
.env
Normal file
14
.env
Normal 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
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
node_modules
|
||||
134
README.md
134
README.md
@ -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
9
emojis.json
Normal 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
27
package.json
Normal 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
126
src/commands/minecraft.js
Normal 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] });
|
||||
}
|
||||
}
|
||||
};
|
||||
40
src/commands/setup-access.js
Normal file
40
src/commands/setup-access.js
Normal 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
47
src/events/ready.js
Normal 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' });
|
||||
},
|
||||
};
|
||||
26
src/handlers/commandHandler.js
Normal file
26
src/handlers/commandHandler.js
Normal 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 };
|
||||
35
src/handlers/eventHandler.js
Normal file
35
src/handlers/eventHandler.js
Normal 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 };
|
||||
191
src/handlers/interactionHandler.js
Normal file
191
src/handlers/interactionHandler.js
Normal 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.'
|
||||
});
|
||||
}
|
||||
}
|
||||
110
src/utils/accessRequestManager.js
Normal file
110
src/utils/accessRequestManager.js
Normal 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;
|
||||
82
src/utils/minecraftServer.js
Normal file
82
src/utils/minecraftServer.js
Normal 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;
|
||||
119
src/utils/whitelistManager.js
Normal file
119
src/utils/whitelistManager.js
Normal 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
33
start.js
Normal 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();
|
||||
Loading…
Reference in New Issue
Block a user