This commit is contained in:
Louis Mazin 2026-02-01 23:27:18 +01:00
parent 8e0dfb33b3
commit c5b95a2698
3 changed files with 151 additions and 13 deletions

View File

@ -0,0 +1,88 @@
const { SlashCommandBuilder, EmbedBuilder, MessageFlags } = require('discord.js');
const { getWebSocketStatus, forceWebSocketReconnect } = require('../../consoleMonitor.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('diagnostique-ws')
.setDescription('Vérifier l\'état du WebSocket et forcer une reconnexion si nécessaire')
.addBooleanOption(option =>
option.setName('reconnect')
.setDescription('Forcer une reconnexion du WebSocket')
.setRequired(false)
),
async execute(interaction) {
try {
const forceReconnect = interaction.options.getBoolean('reconnect') || false;
if (forceReconnect) {
await interaction.reply({
content: '🔄 Reconnexion forcée du WebSocket en cours...',
flags: MessageFlags.Ephemeral
});
await forceWebSocketReconnect();
// Attendre un peu pour laisser le temps de se reconnecter
await new Promise(resolve => setTimeout(resolve, 3000));
}
const status = getWebSocketStatus();
const embed = new EmbedBuilder()
.setColor(status.hasWebSocket && status.wsState === 'OPEN' ? 0x00FF00 : 0xFF0000)
.setTitle('🔌 Diagnostic WebSocket')
.addFields(
{ name: '📊 Monitoring Actif', value: status.isMonitoring ? '✅ Oui' : '❌ Non', inline: true },
{ name: '🔗 WebSocket Présent', value: status.hasWebSocket ? '✅ Oui' : '❌ Non', inline: true },
{ name: '📡 État Connexion', value: status.wsState || 'N/A', inline: true },
{ name: '🔄 En cours de connexion', value: status.isConnecting ? '⏳ Oui' : '✅ Non', inline: true },
{ name: '💓 Heartbeat Actif', value: status.hasHeartbeat ? '✅ Oui' : '❌ Non', inline: true },
{ name: '🔍 Vérification Auto', value: status.hasCheckInterval ? '✅ Oui' : '❌ Non', inline: true },
{ name: '⏱️ Reconnexion Planifiée', value: status.hasPendingReconnect ? '⏳ Oui' : '❌ Non', inline: true },
{ name: '🔁 Délai Reconnexion', value: `${Math.round(status.reconnectDelayMs / 1000)}s`, inline: true },
{ name: '🔄 Refresh Credentials', value: status.hasRefreshInterval ? '✅ Oui' : '❌ Non', inline: true }
)
.setTimestamp();
if (status.connectionTimestamp) {
embed.addFields({ name: '🕐 Dernière Connexion', value: status.connectionTimestamp, inline: false });
}
if (status.monitoringStartTimestamp) {
embed.addFields({ name: '🚀 Démarrage Monitoring', value: status.monitoringStartTimestamp, inline: false });
}
// Recommandations
let recommendations = '';
if (!status.isMonitoring) {
recommendations += '⚠️ Le monitoring n\'est pas actif. Redémarrer le bot.\n';
}
if (!status.hasWebSocket || status.wsState !== 'OPEN') {
recommendations += '⚠️ Le WebSocket n\'est pas connecté. Utilisez `/diagnostique-ws reconnect:True` pour forcer une reconnexion.\n';
}
if (status.hasPendingReconnect) {
recommendations += ` Une reconnexion est prévue dans ${Math.round(status.reconnectDelayMs / 1000)}s.\n`;
}
if (recommendations) {
embed.addFields({ name: '💡 Recommandations', value: recommendations, inline: false });
} else {
embed.addFields({ name: '✅ Statut', value: 'Tout fonctionne normalement !', inline: false });
}
if (forceReconnect) {
await interaction.editReply({ content: null, embeds: [embed], flags: MessageFlags.Ephemeral });
} else {
await interaction.reply({ embeds: [embed], flags: MessageFlags.Ephemeral });
}
} catch (error) {
console.error('[DIAGNOSTIQUE-WS] Erreur:', error);
await interaction.reply({
content: '❌ Erreur lors du diagnostic',
flags: MessageFlags.Ephemeral
}).catch(() => {});
}
},
};

View File

@ -260,39 +260,43 @@ const scheduleReconnect = () => {
if (reconnectTimeout) clearTimeout(reconnectTimeout);
const delay = reconnectDelayMs;
reconnectDelayMs = Math.min(reconnectDelayMs * 2, RECONNECT_DELAY_MAX_MS);
console.log(`🔄 Reconnexion WebSocket dans ${Math.round(delay / 1000)}s...`);
console.log(`🔄 [WEBSOCKET] Reconnexion WebSocket programmée dans ${Math.round(delay / 1000)}s... (Monitoring actif: ${isMonitoring})`);
reconnectTimeout = setTimeout(() => {
console.log('🔄 [WEBSOCKET] Tentative de reconnexion maintenant...');
checkAndManageWebSocket();
}, delay);
};
const connectWebSocket = async (pterodactylToken, serverId) => {
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
console.log('⚠️ WebSocket déjà connecté ou en cours de connexion');
console.log('⚠️ [WEBSOCKET] Déjà connecté ou en cours de connexion (état:', ws.readyState, ')');
return;
}
if (isConnecting) {
console.log('⚠️ Connexion WebSocket déjà en cours');
console.log('⚠️ [WEBSOCKET] Connexion déjà en cours, abandon de cette tentative');
return;
}
isConnecting = true;
connectionTimestamp = Date.now();
console.log(`📅 Timestamp de connexion: ${new Date(connectionTimestamp).toISOString()}`);
console.log(`📅 [WEBSOCKET] Timestamp de connexion: ${new Date(connectionTimestamp).toISOString()}`);
try {
// Toujours rafraîchir les credentials pour éviter les tokens expirés après redémarrage serveur
console.log('🔑 [WEBSOCKET] Récupération de nouveaux credentials...');
const credentials = await getWebSocketCredentials(pterodactylToken, serverId);
console.log('✅ [WEBSOCKET] Credentials récupérés avec succès');
ws = new WebSocket(credentials.socket, {
origin: process.env.PTERODACTYL_API_URL
});
ws.on('open', () => {
console.log('✅ WebSocket Pterodactyl connecté');
console.log('✅ [WEBSOCKET] WebSocket Pterodactyl connecté avec succès');
isConnecting = false;
resetReconnectBackoff();
console.log('🔐 [WEBSOCKET] Envoi de l\'authentification...');
ws.send(JSON.stringify({
event: 'auth',
args: [credentials.token]
@ -304,7 +308,8 @@ const connectWebSocket = async (pterodactylToken, serverId) => {
const message = JSON.parse(data.toString());
if (message.event === 'auth success') {
console.log('✅ Authentification WebSocket réussie');
console.log('✅ [WEBSOCKET] Authentification WebSocket réussie');
console.log('📡 [WEBSOCKET] Demande de réception des logs console...');
ws.send(JSON.stringify({
event: 'send logs',
@ -317,6 +322,7 @@ const connectWebSocket = async (pterodactylToken, serverId) => {
ws.send(JSON.stringify({ event: 'send heartbeat', args: [] }));
}
}, 30000);
console.log('💓 [WEBSOCKET] Heartbeat configuré (30s)');
}
if (message.event === 'console output') {
const log = message.args[0];
@ -340,6 +346,11 @@ const connectWebSocket = async (pterodactylToken, serverId) => {
}
}
// Détecter si c'est un message de chat
if (log.includes('[CHAT]')) {
console.log(`💬 [CONSOLE] Message de chat détecté: ${log.substring(0, 100)}`);
}
// Transférer le message de chat vers Discord via le bridge
console.log(`📋 [CONSOLE] Traitement du log pour le bridge...`);
try {
@ -400,12 +411,15 @@ const connectWebSocket = async (pterodactylToken, serverId) => {
});
ws.on('error', (error) => {
console.error('❌ Erreur WebSocket:', error.message);
console.error('❌ [WEBSOCKET] Erreur WebSocket:', error.message);
console.error('❌ [WEBSOCKET] Code erreur:', error.code || 'N/A');
isConnecting = false;
});
ws.on('close', (code, reason) => {
console.log(`⚠️ WebSocket Pterodactyl déconnecté (Code: ${code})`);
const reasonStr = reason ? reason.toString() : 'Aucune raison fournie';
console.log(`⚠️ [WEBSOCKET] WebSocket Pterodactyl déconnecté (Code: ${code}, Raison: ${reasonStr})`);
console.log(`🔍 [WEBSOCKET] État actuel: isMonitoring=${isMonitoring}, isConnecting=${isConnecting}`);
ws = null;
isConnecting = false;
connectionTimestamp = null;
@ -413,18 +427,28 @@ const connectWebSocket = async (pterodactylToken, serverId) => {
if (heartbeatInterval) {
clearInterval(heartbeatInterval);
heartbeatInterval = null;
console.log('💔 [WEBSOCKET] Heartbeat arrêté');
}
// Planifier une reconnexion avec backoff (utile lors des redémarrages quotidiens du serveur)
scheduleReconnect();
if (isMonitoring) {
scheduleReconnect();
} else {
console.log('⚠️ [WEBSOCKET] Monitoring désactivé, pas de reconnexion');
}
});
} catch (error) {
console.error('Erreur lors de la connexion WebSocket:', error);
console.error('❌ [WEBSOCKET] Erreur lors de la connexion WebSocket:', error.message);
console.error('❌ [WEBSOCKET] Stack:', error.stack);
isConnecting = false;
connectionTimestamp = null;
// Échec immédiat -> planifier reconnexion avec backoff
scheduleReconnect();
if (isMonitoring) {
scheduleReconnect();
} else {
console.log('⚠️ [WEBSOCKET] Monitoring désactivé, pas de reconnexion');
}
}
};
@ -528,4 +552,29 @@ const forceWebSocketReconnect = async () => {
await forceReconnectToRefreshCredentials();
};
module.exports = { startConsoleMonitoring, stopConsoleMonitoring, forceWebSocketReconnect };
// Fonction pour obtenir l'état du WebSocket (pour diagnostic)
const getWebSocketStatus = () => {
const wsStates = {
[WebSocket.CONNECTING]: 'CONNECTING',
[WebSocket.OPEN]: 'OPEN',
[WebSocket.CLOSING]: 'CLOSING',
[WebSocket.CLOSED]: 'CLOSED'
};
return {
isMonitoring,
isConnecting,
hasWebSocket: !!ws,
wsState: ws ? wsStates[ws.readyState] : 'N/A',
wsStateRaw: ws ? ws.readyState : null,
connectionTimestamp: connectionTimestamp ? new Date(connectionTimestamp).toISOString() : null,
monitoringStartTimestamp: monitoringStartTimestamp ? new Date(monitoringStartTimestamp).toISOString() : null,
reconnectDelayMs,
hasHeartbeat: !!heartbeatInterval,
hasCheckInterval: !!checkInterval,
hasRefreshInterval: !!refreshCredentialsInterval,
hasPendingReconnect: !!reconnectTimeout
};
};
module.exports = { startConsoleMonitoring, stopConsoleMonitoring, forceWebSocketReconnect, getWebSocketStatus };

View File

@ -9,7 +9,7 @@ const initPalworldBridge = (client, channelId) => {
bridgeClient = client;
bridgeChannelId = channelId;
console.log(`🌉 Pont Palworld-Discord initialisé (Salon: ${channelId})`);
console.log(`🌉 [BRIDGE] Pont Palworld-Discord initialisé (Salon: ${channelId})`);
// Écouter les messages du salon Discord
client.on('messageCreate', async (message) => {
@ -23,6 +23,7 @@ const initPalworldBridge = (client, channelId) => {
try {
// Récupérer le pseudo Palworld lié
console.log(`🔍 [BRIDGE] Recherche de liaison pour ${message.author.tag} (ID: ${message.author.id})`);
const userLink = await getUserLink(message.author.id);
console.log(`🔍 [BRIDGE] Liaison trouvée pour ${message.author.tag}:`, userLink);