test
This commit is contained in:
parent
40c76d4872
commit
602321529c
@ -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);
|
||||
```
|
||||
58
README.md
58
README.md
@ -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)
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user