From 602321529ce71ce6d84de30293c2e8ede0ab8039 Mon Sep 17 00:00:00 2001 From: Louis Mazin Date: Mon, 2 Feb 2026 00:42:49 +0100 Subject: [PATCH] test --- RAM_MONITOR_README.md | 146 ------------------------------------------ README.md | 58 ----------------- consoleMonitor.js | 70 +++++++++++++++----- palworld-bridge.js | 21 ++++-- 4 files changed, 71 insertions(+), 224 deletions(-) delete mode 100644 RAM_MONITOR_README.md delete mode 100644 README.md diff --git a/RAM_MONITOR_README.md b/RAM_MONITOR_README.md deleted file mode 100644 index f9ace98..0000000 --- a/RAM_MONITOR_README.md +++ /dev/null @@ -1,146 +0,0 @@ -# 🔍 Moniteur de RAM - Palworld Server - -## Description - -Le moniteur de RAM surveille automatiquement l'utilisation de la mémoire du serveur Palworld. Lorsque le seuil de **19 Go** est dépassé, le serveur est automatiquement redémarré pour éviter les problèmes de performance ou de crash. - -## Fonctionnalités - -### Surveillance automatique -- ✅ Vérification de la RAM toutes les 60 secondes -- ✅ Seuil de déclenchement : **19 Go** -- ✅ Redémarrage automatique si le seuil est dépassé -- ✅ Resynchronisation automatique du WebSocket après redémarrage - -### Processus de redémarrage -Lorsque le seuil est atteint, le système effectue les étapes suivantes : - -1. 💾 **Sauvegarde** du serveur -2. 📢 **Annonce** aux joueurs (broadcast : "Redémarrage automatique - RAM élevée - 30 secondes") -3. ⏹️ **Arrêt** du serveur (signal stop) -4. ⏳ **Attente** de 25 secondes -5. 🚀 **Redémarrage** du serveur (signal start) -6. ⏳ **Vérification** jusqu'à ce que le serveur soit opérationnel -7. 🔄 **Resynchronisation** du WebSocket (après 5 secondes supplémentaires) - -### Commande Discord - -#### `/ram-status` -Affiche l'état actuel de la RAM du serveur. - -**Permissions requises :** Rôle Rygainland - -**Informations affichées :** -- 📊 Utilisation actuelle (en Go et pourcentage) -- ⚠️ Seuil de redémarrage configuré -- 🔄 État du serveur (running, stopped, etc.) -- 💡 Niveau d'alerte (Normal 🟢 / Élevé 🟡 / Critique 🔴) -- ✅ État de la surveillance automatique - -**Exemple d'affichage :** -``` -🟢 État de la RAM du serveur Palworld - -📊 Utilisation actuelle: 15.32 Go / 19.00 Go (80.6%) -⚠️ Seuil de redémarrage: 19.00 Go -🔄 État du serveur: running -💡 Niveau: Élevée - -✅ Surveillance automatique active -``` - -## Configuration - -### Variables d'environnement requises - -Le moniteur utilise les variables d'environnement suivantes (déjà configurées) : - -```env -PTERODACTYL_API_URL=https://panel.louismazin.ovh -PTERODACTYL_API_TOKEN=votre_token -PTERODACTYL_SERVER_ID=ae4a628f -``` - -### Paramètres modifiables - -Dans le fichier `ramMonitor.js` : - -```javascript -const RAM_THRESHOLD_MB = 19 * 1024; // Seuil en MB (19 Go par défaut) -const CHECK_INTERVAL_MS = 60 * 1000; // Intervalle de vérification (60 secondes) -``` - -## Intégration - -### Fichiers modifiés/créés - -1. **`ramMonitor.js`** (nouveau) - - Module principal de surveillance - - Gestion du redémarrage automatique - -2. **`index.js`** (modifié) - - Ajout de `startRAMMonitoring(forceWebSocketReconnect)` - - Import du module ramMonitor - -3. **`consoleMonitor.js`** (modifié) - - Ajout de la fonction `forceWebSocketReconnect()` - - Export de la fonction pour utilisation par ramMonitor - -4. **`commands/server/ram-status.js`** (nouveau) - - Commande Discord pour consulter l'état de la RAM - -## Logs - -Le moniteur affiche des logs détaillés dans la console : - -``` -🚀 [RAM Monitor] Démarrage de la surveillance RAM (seuil: 19 Go) -⏱️ [RAM Monitor] Intervalle de vérification: 60s -🔍 [RAM Monitor] Utilisation RAM: 15.32 Go / État: running -⚠️ [RAM Monitor] SEUIL DÉPASSÉ ! 19.54 Go > 19 Go -🔄 [RAM Monitor] Déclenchement du redémarrage automatique... -💾 [RAM Monitor] Sauvegarde du serveur... -📢 [RAM Monitor] Annonce du redémarrage... -⏹️ [RAM Monitor] Arrêt du serveur... -⏳ [RAM Monitor] Attente de 25 secondes... -🚀 [RAM Monitor] Redémarrage du serveur... -✅ [RAM Monitor] Serveur redémarré avec succès ! -🔄 [RAM Monitor] Resynchronisation du WebSocket... -``` - -## Sécurité - -- 🔒 Un seul redémarrage ne peut être en cours à la fois (protection via flag `isRebooting`) -- 🔒 Timeout maximum de 2.5 minutes pour le redémarrage -- 🔒 Vérification de l'état du serveur avant déclenchement (doit être `running`) -- 🔒 Commande Discord protégée par rôle - -## Notes techniques - -- Le serveur Pterodactyl fournit `memory_bytes` qui est converti en Mo puis Go -- La surveillance ne se déclenche que si le serveur est en état `running` -- Le WebSocket est resynchronisé après 5 secondes de stabilisation post-redémarrage -- Les websockets existants sont proprement fermés avant reconnexion - -## Dépannage - -### La surveillance ne démarre pas -- Vérifier les variables d'environnement `PTERODACTYL_API_TOKEN` et `PTERODACTYL_SERVER_ID` -- Vérifier les permissions API Pterodactyl - -### Le redémarrage ne se déclenche pas -- Vérifier que le seuil est correctement configuré -- Consulter les logs pour voir l'utilisation actuelle -- Utiliser `/ram-status` pour diagnostiquer - -### Le WebSocket ne se reconnecte pas -- Vérifier que `consoleMonitor.js` est bien démarré -- Les logs indiqueront les tentatives de reconnexion - -## Désactivation - -Pour désactiver temporairement la surveillance, commenter dans `index.js` : - -```javascript -// startRAMMonitoring(forceWebSocketReconnect); -``` diff --git a/README.md b/README.md deleted file mode 100644 index 077f2a3..0000000 --- a/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# rygain_bot - -Bot Discord pour la gestion du serveur Palworld Rygainland. - -## Fonctionnalités - -### 🔗 Système de liaison Discord-Palworld -- Liaison automatique des comptes Discord et Palworld via code à 6 chiffres -- Gestion automatique des rôles lors de la liaison/déliaison -- Suivi des dernières connexions - -### 🌉 Pont Discord-Palworld -- Messages Discord → Palworld (broadcast) -- Messages Palworld → Discord (avec avatar et pseudo Discord pour les comptes liés) -- Salon dédié : `1467491354924814411` - -### 📊 Statistiques serveur -- Affichage des joueurs connectés -- Statistiques du serveur Palworld - -### 🔧 Commandes disponibles -- `/lier-rygainland` - Lier votre compte Discord à Palworld -- `/delier-rygainland` - Délier votre compte -- `/lier` (Admin) - Lier manuellement un compte -- `/delier` (Admin) - Délier manuellement un compte -- `/server-stats` - Afficher les stats du serveur -- `/trad` - Traduire un message - -## Installation - -1. Cloner le repo -2. Copier `.env.example` vers `.env` -3. Configurer les variables d'environnement -4. Installer les dépendances : `npm install` -5. Lancer le bot : `node index.js` - -## Configuration - -Voir le fichier `.env.example` pour la liste complète des variables d'environnement. - -Variables importantes : -- `BRIDGE_CHANNEL_ID` - ID du salon Discord pour le pont Palworld (défaut: 1467491354924814411) -- `GUILD_ID` - ID du serveur Discord -- `PALWORLD_API_TOKEN` - Token API REST de Palworld -- `PTERODACTYL_API_TOKEN` - Token API Pterodactyl - -## Débogage - -Le bot affiche des logs détaillés préfixés par : -- `[BRIDGE]` - Logs du pont Discord-Palworld -- `[CONSOLE]` - Logs du monitoring de la console Pterodactyl -- `[LIER-RYGAINLAND]` - Logs de la commande de liaison - -Si le pont ne fonctionne pas, vérifiez : -1. Que le `BRIDGE_CHANNEL_ID` est correct dans `.env` -2. Que le bot a les permissions d'envoyer des messages et créer des webhooks dans le salon -3. Les logs pour voir si les messages sont détectés -4. Que l'API Palworld est accessible (http://play.louismazin.ovh:8212) diff --git a/consoleMonitor.js b/consoleMonitor.js index 7d3bb93..cda2972 100644 --- a/consoleMonitor.js +++ b/consoleMonitor.js @@ -297,6 +297,7 @@ const connectWebSocket = async (pterodactylToken, serverId) => { console.log('✅ [WEBSOCKET] WebSocket Pterodactyl connecté avec succès'); isConnecting = false; resetReconnectBackoff(); + lastHeartbeatResponse = Date.now(); // Initialiser à maintenant console.log('🔐 [WEBSOCKET] Envoi de l\'authentification...'); ws.send(JSON.stringify({ @@ -304,7 +305,14 @@ const connectWebSocket = async (pterodactylToken, serverId) => { args: [credentials.token] })); }); + + // Handler pour le pong WebSocket natif + ws.on('pong', () => { + lastHeartbeatResponse = Date.now(); + console.log('🏓 [WEBSOCKET] Pong reçu - connexion OK'); + }); + ws.on('message', async (data) => { try { const message = JSON.parse(data.toString()); @@ -318,38 +326,70 @@ const connectWebSocket = async (pterodactylToken, serverId) => { args: [null] })); - // Configurer le heartbeat + // Configurer le heartbeat avec détection agressive if (heartbeatInterval) clearInterval(heartbeatInterval); if (heartbeatTimeout) clearTimeout(heartbeatTimeout); lastHeartbeatResponse = Date.now(); + let missedHeartbeats = 0; + const MAX_MISSED_HEARTBEATS = 2; // 2 heartbeats manqués = reconnexion heartbeatInterval = setInterval(() => { if (ws && ws.readyState === WebSocket.OPEN) { try { + const timeSinceLastResponse = Date.now() - lastHeartbeatResponse; + + // Si on a pas eu de réponse depuis plus de 30s, incrémenter le compteur + if (timeSinceLastResponse > 30000) { + missedHeartbeats++; + console.warn(`⚠️ [WEBSOCKET] Heartbeat manqué (${missedHeartbeats}/${MAX_MISSED_HEARTBEATS}). Dernière réponse: ${Math.round(timeSinceLastResponse/1000)}s`); + + if (missedHeartbeats >= MAX_MISSED_HEARTBEATS) { + console.error('💔 [WEBSOCKET] Trop de heartbeats manqués! Reconnexion forcée...'); + if (ws) { + ws.terminate(); // Fermeture immédiate sans attendre + } + return; + } + } else { + // Reset le compteur si on a reçu une réponse récemment + if (missedHeartbeats > 0) { + console.log('✅ [WEBSOCKET] Heartbeat de retour à la normale'); + missedHeartbeats = 0; + } + } + + // Envoyer le heartbeat ws.send(JSON.stringify({ event: 'send heartbeat', args: [] })); console.log('💓 [WEBSOCKET] Heartbeat envoyé'); - // Configurer un timeout pour détecter si le serveur ne répond plus - if (heartbeatTimeout) clearTimeout(heartbeatTimeout); - heartbeatTimeout = setTimeout(() => { - const timeSinceLastResponse = Date.now() - lastHeartbeatResponse; - if (timeSinceLastResponse > 60000) { // 1 minute sans réponse - console.error('💔 [WEBSOCKET] Timeout: aucune réponse du serveur depuis 1 minute'); - console.log('🔄 [WEBSOCKET] Fermeture et reconnexion du WebSocket...'); - if (ws) { - ws.close(); - } - } - }, 60000); + // Aussi envoyer un ping WebSocket natif pour vérifier la connexion TCP + if (ws.ping) { + ws.ping(); + console.log('🏓 [WEBSOCKET] Ping natif envoyé'); + } + } catch (error) { console.error('❌ [WEBSOCKET] Erreur lors de l\'envoi du heartbeat:', error.message); + missedHeartbeats++; + if (missedHeartbeats >= MAX_MISSED_HEARTBEATS && ws) { + console.error('💔 [WEBSOCKET] Erreurs répétées, reconnexion forcée...'); + ws.terminate(); + } } } else { console.warn('⚠️ [WEBSOCKET] Heartbeat impossible: WebSocket non ouvert (état:', ws ? ws.readyState : 'null', ')'); + missedHeartbeats++; + if (missedHeartbeats >= MAX_MISSED_HEARTBEATS) { + console.error('💔 [WEBSOCKET] WebSocket fermé, nettoyage...'); + if (heartbeatInterval) { + clearInterval(heartbeatInterval); + heartbeatInterval = null; + } + } } - }, 30000); - console.log('💓 [WEBSOCKET] Heartbeat configuré (30s avec timeout de 60s)'); + }, 20000); // Vérifier toutes les 20s au lieu de 30s + console.log('💓 [WEBSOCKET] Heartbeat configuré (20s, max 2 manqués)'); } // Détecter les réponses du serveur (console output, status, etc) pour mettre à jour le lastHeartbeatResponse diff --git a/palworld-bridge.js b/palworld-bridge.js index 88fb89d..6fd1421 100644 --- a/palworld-bridge.js +++ b/palworld-bridge.js @@ -65,8 +65,10 @@ const sendToPalworld = async (palworldUsername, content) => { // Format du message pour Palworld const broadcastMessage = `[Discord] ${palworldUsername}: ${cleanContent}`; - // Envoyer via l'API Palworld - await axios({ + console.log(`🚀 [BRIDGE] Envoi HTTP vers Palworld: ${broadcastMessage}`); + + // Envoyer via l'API Palworld avec timeout + const response = await axios({ method: 'post', url: 'http://play.louismazin.ovh:8212/v1/api/announce', headers: { @@ -76,13 +78,22 @@ const sendToPalworld = async (palworldUsername, content) => { }, data: { message: broadcastMessage - } + }, + timeout: 10000 // Timeout de 10s }); - console.log(`✅ Message envoyé à Palworld: ${broadcastMessage}`); + console.log(`✅ [BRIDGE] Message envoyé à Palworld (statut ${response.status}): ${broadcastMessage}`); } catch (error) { - console.error('Erreur lors de l\'envoi vers Palworld:', error.message); + if (error.code === 'ECONNABORTED') { + console.error('❌ [BRIDGE] Timeout lors de l\'envoi vers Palworld (10s)'); + } else if (error.code === 'ECONNREFUSED') { + console.error('❌ [BRIDGE] Connexion refusée par le serveur Palworld'); + } else if (error.response) { + console.error(`❌ [BRIDGE] Erreur HTTP ${error.response.status}:`, error.response.data); + } else { + console.error('❌ [BRIDGE] Erreur lors de l\'envoi vers Palworld:', error.message); + } throw error; } };