const axios = require('axios'); const WebSocket = require('ws'); const { verifyLinkCode, updateUserLinkWithUsername } = require('./database.js'); let ws = null; let reconnectTimeout = null; let client = null; let heartbeatInterval = null; const parseLogMessage = (log) => { // Exemple de format de log Palworld: // [2024.01.15-12:34:56] PlayerName (76561198012345678): !link ABC123 const linkRegex = /\[.*?\]\s*(.+?)\s*\((\d{17})\).*?!link\s+([A-Z0-9]{6})/i; const match = log.match(linkRegex); console.log('Parsing log:', log); console.log('Regex match:', match); if (match) { return { playerName: match[1].trim(), steamId: match[2], code: match[3].toUpperCase() }; } return null; }; const handleLinkCommand = async (playerName, steamId, code) => { try { console.log(`🔗 Tentative de liaison détectée: ${playerName} (${steamId}) avec le code ${code}`); const result = await verifyLinkCode(code, steamId, playerName); if (result.success) { console.log(`✅ Liaison réussie pour ${playerName}`); // Envoyer un message de confirmation à l'utilisateur Discord if (client) { const user = await client.users.fetch(result.discordId).catch(() => null); if (user) { await updateUserLinkWithUsername(result.discordId, user.tag); await user.send( `✅ **Liaison réussie !**\n\n` + `Votre compte Discord a été lié avec succès à votre compte Palworld:\n` + `🎮 Nom Palworld: **${playerName}**\n` + `🆔 Steam ID: \`${steamId}\`` ).catch(() => {}); } } } else { console.log(`❌ Échec de la liaison: ${result.message}`); } } catch (error) { console.error('Erreur lors du traitement de la commande !link:', error); } }; const getWebSocketCredentials = async (pterodactylToken, serverId) => { try { const response = await axios({ method: 'get', url: `${process.env.PTERODACTYL_API_URL}/api/client/servers/${serverId}/websocket`, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': `Bearer ${pterodactylToken}` } }); return response.data.data; } catch (error) { console.error('Erreur lors de la récupération des credentials WebSocket:', error.message); throw error; } }; const connectWebSocket = async (pterodactylToken, serverId) => { try { const credentials = await getWebSocketCredentials(pterodactylToken, serverId); ws = new WebSocket(credentials.socket, { origin: process.env.PTERODACTYL_API_URL }); ws.on('open', () => { console.log('✅ WebSocket Pterodactyl connecté'); // Authentification selon la documentation ws.send(JSON.stringify({ event: 'auth', args: [credentials.token] })); }); ws.on('message', (data) => { try { const message = JSON.parse(data.toString()); // Gérer l'événement d'authentification réussie if (message.event === 'auth success') { console.log('✅ Authentification WebSocket réussie'); // S'abonner aux logs de la console ws.send(JSON.stringify({ event: 'send logs', args: [null] })); // Démarrer le heartbeat (toutes les 30 secondes) if (heartbeatInterval) clearInterval(heartbeatInterval); heartbeatInterval = setInterval(() => { if (ws && ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ event: 'send heartbeat', args: [] })); } }, 30000); } // Gérer les logs de la console if (message.event === 'console output') { const log = message.args[0]; // Afficher le log pour debug console.log('📝 Log:', log); // Détecter les commandes !link const linkData = parseLogMessage(log); if (linkData) { handleLinkCommand(linkData.playerName, linkData.steamId, linkData.code); } } // Gérer les événements de statut if (message.event === 'status') { console.log('📊 Statut du serveur:', message.args[0]); } } catch (error) { // Message non-JSON ou erreur de parsing, on ignore console.error('Erreur parsing message WebSocket:', error); } }); ws.on('error', (error) => { console.error('❌ Erreur WebSocket:', error.message); }); ws.on('close', (code, reason) => { console.log(`⚠️ WebSocket Pterodactyl déconnecté (Code: ${code}, Raison: ${reason})`); ws = null; // Arrêter le heartbeat if (heartbeatInterval) { clearInterval(heartbeatInterval); heartbeatInterval = null; } // Reconnexion automatique if (reconnectTimeout) clearTimeout(reconnectTimeout); reconnectTimeout = setTimeout(() => { console.log('🔄 Tentative de reconnexion...'); connectWebSocket(pterodactylToken, serverId); }, 10000); }); } catch (error) { console.error('Erreur lors de la connexion WebSocket:', error); // Réessayer dans 30 secondes if (reconnectTimeout) clearTimeout(reconnectTimeout); reconnectTimeout = setTimeout(() => { console.log('🔄 Tentative de reconnexion...'); connectWebSocket(pterodactylToken, serverId); }, 30000); } }; const startConsoleMonitoring = (discordClient, pterodactylToken) => { client = discordClient; const serverId = process.env.PTERODACTYL_SERVER_ID; if (!serverId) { console.error('❌ PTERODACTYL_SERVER_ID non défini dans .env'); return; } console.log('🔍 Surveillance de la console Pterodactyl démarrée'); connectWebSocket(pterodactylToken, serverId); }; const stopConsoleMonitoring = () => { if (heartbeatInterval) { clearInterval(heartbeatInterval); heartbeatInterval = null; } if (reconnectTimeout) { clearTimeout(reconnectTimeout); reconnectTimeout = null; } if (ws) { ws.close(); ws = null; } }; module.exports = { startConsoleMonitoring, stopConsoleMonitoring };