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');
|
console.log('✅ [WEBSOCKET] WebSocket Pterodactyl connecté avec succès');
|
||||||
isConnecting = false;
|
isConnecting = false;
|
||||||
resetReconnectBackoff();
|
resetReconnectBackoff();
|
||||||
|
lastHeartbeatResponse = Date.now(); // Initialiser à maintenant
|
||||||
|
|
||||||
console.log('🔐 [WEBSOCKET] Envoi de l\'authentification...');
|
console.log('🔐 [WEBSOCKET] Envoi de l\'authentification...');
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
@ -305,6 +306,13 @@ const connectWebSocket = async (pterodactylToken, serverId) => {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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) => {
|
ws.on('message', async (data) => {
|
||||||
try {
|
try {
|
||||||
const message = JSON.parse(data.toString());
|
const message = JSON.parse(data.toString());
|
||||||
@ -318,38 +326,70 @@ const connectWebSocket = async (pterodactylToken, serverId) => {
|
|||||||
args: [null]
|
args: [null]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Configurer le heartbeat
|
// Configurer le heartbeat avec détection agressive
|
||||||
if (heartbeatInterval) clearInterval(heartbeatInterval);
|
if (heartbeatInterval) clearInterval(heartbeatInterval);
|
||||||
if (heartbeatTimeout) clearTimeout(heartbeatTimeout);
|
if (heartbeatTimeout) clearTimeout(heartbeatTimeout);
|
||||||
|
|
||||||
lastHeartbeatResponse = Date.now();
|
lastHeartbeatResponse = Date.now();
|
||||||
|
let missedHeartbeats = 0;
|
||||||
|
const MAX_MISSED_HEARTBEATS = 2; // 2 heartbeats manqués = reconnexion
|
||||||
|
|
||||||
heartbeatInterval = setInterval(() => {
|
heartbeatInterval = setInterval(() => {
|
||||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||||
try {
|
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: [] }));
|
ws.send(JSON.stringify({ event: 'send heartbeat', args: [] }));
|
||||||
console.log('💓 [WEBSOCKET] Heartbeat envoyé');
|
console.log('💓 [WEBSOCKET] Heartbeat envoyé');
|
||||||
|
|
||||||
// Configurer un timeout pour détecter si le serveur ne répond plus
|
// Aussi envoyer un ping WebSocket natif pour vérifier la connexion TCP
|
||||||
if (heartbeatTimeout) clearTimeout(heartbeatTimeout);
|
if (ws.ping) {
|
||||||
heartbeatTimeout = setTimeout(() => {
|
ws.ping();
|
||||||
const timeSinceLastResponse = Date.now() - lastHeartbeatResponse;
|
console.log('🏓 [WEBSOCKET] Ping natif envoyé');
|
||||||
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);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ [WEBSOCKET] Erreur lors de l\'envoi du heartbeat:', error.message);
|
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 {
|
} else {
|
||||||
console.warn('⚠️ [WEBSOCKET] Heartbeat impossible: WebSocket non ouvert (état:', ws ? ws.readyState : 'null', ')');
|
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
|
// 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
|
// Format du message pour Palworld
|
||||||
const broadcastMessage = `[Discord] ${palworldUsername}: ${cleanContent}`;
|
const broadcastMessage = `[Discord] ${palworldUsername}: ${cleanContent}`;
|
||||||
|
|
||||||
// Envoyer via l'API Palworld
|
console.log(`🚀 [BRIDGE] Envoi HTTP vers Palworld: ${broadcastMessage}`);
|
||||||
await axios({
|
|
||||||
|
// Envoyer via l'API Palworld avec timeout
|
||||||
|
const response = await axios({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: 'http://play.louismazin.ovh:8212/v1/api/announce',
|
url: 'http://play.louismazin.ovh:8212/v1/api/announce',
|
||||||
headers: {
|
headers: {
|
||||||
@ -76,13 +78,22 @@ const sendToPalworld = async (palworldUsername, content) => {
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
message: broadcastMessage
|
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) {
|
} 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;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user