diff --git a/commands/utility/delier-rygainland.js b/commands/utility/delier-rygainland.js index b1d7ffb..f637859 100644 --- a/commands/utility/delier-rygainland.js +++ b/commands/utility/delier-rygainland.js @@ -68,6 +68,19 @@ module.exports = { // Supprimer la liaison await deleteUserLink(interaction.user.id); + // Retirer le rôle de joueur lié + try { + const guild = interaction.guild; + const member = await guild.members.fetch(interaction.user.id); + const linkedRole = guild.roles.cache.get('1467491093649035475'); + if (linkedRole && member.roles.cache.has(linkedRole.id)) { + await member.roles.remove(linkedRole); + console.log(`✅ Rôle retiré de ${interaction.user.tag}`); + } + } catch (roleError) { + console.error('Erreur lors du retrait du rôle:', roleError); + } + const successEmbed = new EmbedBuilder() .setColor(0x00FF00) .setTitle('✅ Déliaison réussie') diff --git a/commands/utility/delier.js b/commands/utility/delier.js index ead33ff..06b4e5b 100644 --- a/commands/utility/delier.js +++ b/commands/utility/delier.js @@ -68,6 +68,19 @@ module.exports = { if (confirmation.customId === 'admin_confirm_unlink') { await deleteUserLink(discordUser.id); + // Retirer le rôle de joueur lié + try { + const guild = interaction.guild; + const member = await guild.members.fetch(discordUser.id); + const linkedRole = guild.roles.cache.get('1467491093649035475'); + if (linkedRole && member.roles.cache.has(linkedRole.id)) { + await member.roles.remove(linkedRole); + console.log(`✅ Rôle retiré de ${discordUser.tag}`); + } + } catch (roleError) { + console.error('Erreur lors du retrait du rôle:', roleError); + } + const successEmbed = new EmbedBuilder() .setColor(0x00FF00) .setTitle('✅ Liaison supprimée') diff --git a/commands/utility/lier.js b/commands/utility/lier.js index c2aeda0..a4559ee 100644 --- a/commands/utility/lier.js +++ b/commands/utility/lier.js @@ -65,6 +65,19 @@ module.exports = { if (result.success) { await updateUserLinkWithUsername(discordUser.id, discordUser.tag); + // Ajouter le rôle de joueur lié + try { + const guild = interaction.guild; + const member = await guild.members.fetch(discordUser.id); + const linkedRole = guild.roles.cache.get('1467491093649035475'); + if (linkedRole && !member.roles.cache.has(linkedRole.id)) { + await member.roles.add(linkedRole); + console.log(`✅ Rôle ajouté à ${discordUser.tag}`); + } + } catch (roleError) { + console.error('Erreur lors de l\'ajout du rôle:', roleError); + } + const embed = new EmbedBuilder() .setColor(0x00FF00) .setTitle('✅ Liaison manuelle réussie') diff --git a/consoleMonitor.js b/consoleMonitor.js index 550f78e..fc53974 100644 --- a/consoleMonitor.js +++ b/consoleMonitor.js @@ -1,6 +1,7 @@ const axios = require('axios'); const WebSocket = require('ws'); const { verifyLinkCode, updateUserLinkWithUsername, updateLastConnection } = require('./database.js'); +const { handlePalworldChat } = require('./palworld-bridge.js'); let ws = null; let reconnectTimeout = null; @@ -102,6 +103,22 @@ const handleLinkCommand = async (playerName, playerData, code) => { const user = await client.users.fetch(result.discordId).catch(() => null); if (user) { await updateUserLinkWithUsername(result.discordId, user.tag); + + // Ajouter le rôle de joueur lié + try { + const guild = client.guilds.cache.get(process.env.GUILD_ID); + if (guild) { + const member = await guild.members.fetch(result.discordId); + const linkedRole = guild.roles.cache.get('1467491093649035475'); + if (linkedRole && !member.roles.cache.has(linkedRole.id)) { + await member.roles.add(linkedRole); + console.log(`✅ Rôle ajouté à ${user.tag}`); + } + } + } catch (roleError) { + console.error('Erreur lors de l\'ajout du rôle:', roleError); + } + await user.send( `✅ **Liaison réussie !**\n\n` + `Votre compte Discord a été lié avec succès à votre compte Palworld:\n` + @@ -321,6 +338,9 @@ const connectWebSocket = async (pterodactylToken, serverId) => { } } + // Transférer le message de chat vers Discord via le bridge + await handlePalworldChat(log); + const linkData = parseLogMessage(log); if (linkData) { diff --git a/index.js b/index.js index 9e2dba6..de6bab2 100644 --- a/index.js +++ b/index.js @@ -8,6 +8,7 @@ const clean = require('./cleaner.js'); const { Client, GatewayIntentBits, Collection, Events, Partials } = require('discord.js'); const { initDatabase, createTables, cleanExpiredCodes } = require('./database.js'); const { startConsoleMonitoring } = require('./consoleMonitor.js'); +const { initPalworldBridge } = require('./palworld-bridge.js'); const client = new Client({ intents: [ @@ -62,6 +63,10 @@ client.once('ready', async () => { console.log('📦 Système de liaison activé'); + // Initialiser le pont Palworld-Discord + const bridgeChannelId = '1467491354924814411'; + initPalworldBridge(client, bridgeChannelId); + startConsoleMonitoring(client, process.env.PTERODACTYL_API_TOKEN); } catch (error) { console.error('⚠️ Erreur lors de l\'initialisation de la base de données'); diff --git a/palworld-bridge.js b/palworld-bridge.js new file mode 100644 index 0000000..a0e3052 --- /dev/null +++ b/palworld-bridge.js @@ -0,0 +1,163 @@ +const axios = require('axios'); +const { getUserLink, getAllLinks } = require('./database.js'); + +let bridgeClient = null; +let bridgeChannelId = null; +let wsInstance = null; + +// Initialiser le bridge +const initPalworldBridge = (client, channelId, ws) => { + bridgeClient = client; + bridgeChannelId = channelId; + wsInstance = ws; + + console.log('🌉 Pont Palworld-Discord initialisé'); + + // Écouter les messages du salon Discord + client.on('messageCreate', async (message) => { + // Ignorer les messages du bot lui-même + if (message.author.bot) return; + + // Vérifier que c'est bien le salon du pont + if (message.channelId !== bridgeChannelId) return; + + try { + // Récupérer le pseudo Palworld lié + const userLink = await getUserLink(message.author.id); + + if (!userLink) { + // L'utilisateur n'est pas lié + await message.reply('❌ Vous devez lier votre compte Palworld pour envoyer des messages. Utilisez `/lier-rygainland`'); + return; + } + + // Envoyer le message vers Palworld via broadcast + await sendToPalworld(userLink.palworld_username, message.content); + + } catch (error) { + console.error('Erreur lors de l\'envoi du message vers Palworld:', error); + await message.reply('❌ Erreur lors de l\'envoi du message vers Palworld').catch(() => {}); + } + }); +}; + +// Envoyer un message vers Palworld via RCON broadcast +const sendToPalworld = async (palworldUsername, content) => { + try { + // Nettoyer le message (enlever les mentions, emojis personnalisés, etc.) + let cleanContent = content + .replace(/<@!?\d+>/g, '@utilisateur') // Remplacer les mentions + .replace(/<#\d+>/g, '#salon') // Remplacer les mentions de salons + .replace(/<:.+?:\d+>/g, '') // Enlever les emojis personnalisés + .replace(/\n/g, ' ') // Remplacer les sauts de ligne + .trim(); + + // Limiter la longueur du message + if (cleanContent.length > 200) { + cleanContent = cleanContent.substring(0, 197) + '...'; + } + + // Format du message pour Palworld + const broadcastMessage = `[Discord] ${palworldUsername}: ${cleanContent}`; + + // Envoyer via l'API Palworld + await axios({ + method: 'post', + url: 'http://play.louismazin.ovh:8212/v1/api/announce', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Basic ${process.env.PALWORLD_API_TOKEN}` + }, + data: { + message: broadcastMessage + } + }); + + console.log(`✅ Message envoyé à Palworld: ${broadcastMessage}`); + + } catch (error) { + console.error('Erreur lors de l\'envoi vers Palworld:', error.message); + throw error; + } +}; + +// Parser les messages de chat Palworld et les envoyer sur Discord +const parsePalworldChatAndSend = async (log) => { + if (!bridgeClient || !bridgeChannelId) return; + + // Format: [2026-02-01 13:08:02] [CHAT] coucou + const chatRegex = /\[.*?\]\s*\[CHAT\]\s*<(.+?)>\s*(.+)/i; + const match = log.match(chatRegex); + + if (!match) return; + + const palworldUsername = match[1].trim(); + const messageContent = match[2].trim(); + + // Ignorer les messages de commande !lier + if (messageContent.toLowerCase().startsWith('!lier')) return; + + try { + // Récupérer le salon + const channel = await bridgeClient.channels.fetch(bridgeChannelId).catch(() => null); + if (!channel) { + console.error('❌ Impossible de trouver le salon du pont'); + return; + } + + // Chercher si le joueur a un compte Discord lié + const allLinks = await getAllLinks(); + const linkedUser = allLinks.find(link => link.palworld_username === palworldUsername); + + if (linkedUser) { + // Utilisateur lié : utiliser webhook pour afficher sa PP et son pseudo Discord + try { + const discordUser = await bridgeClient.users.fetch(linkedUser.discord_id).catch(() => null); + + if (discordUser) { + // Créer ou récupérer un webhook pour ce salon + const webhooks = await channel.fetchWebhooks(); + let webhook = webhooks.find(wh => wh.name === 'Palworld Bridge'); + + if (!webhook) { + webhook = await channel.createWebhook({ + name: 'Palworld Bridge', + avatar: 'https://i.imgur.com/AfFp7pu.png' // Logo Palworld + }); + } + + // Envoyer via webhook avec le pseudo et l'avatar Discord + await webhook.send({ + content: messageContent, + username: discordUser.username, + avatarURL: discordUser.displayAvatarURL({ dynamic: true, size: 256 }) + }); + + console.log(`✅ Message Palworld envoyé sur Discord (via webhook): ${discordUser.username}: ${messageContent}`); + return; + } + } catch (error) { + console.error('Erreur lors de l\'envoi via webhook:', error); + // Fallback vers message normal + } + } + + // Utilisateur non lié ou erreur webhook : envoyer un message normal + await channel.send(`**${palworldUsername}**: ${messageContent}`); + console.log(`✅ Message Palworld envoyé sur Discord: ${palworldUsername}: ${messageContent}`); + + } catch (error) { + console.error('Erreur lors de l\'envoi du message Palworld vers Discord:', error); + } +}; + +// Fonction appelée par consoleMonitor pour traiter les messages de chat +const handlePalworldChat = async (log) => { + await parsePalworldChatAndSend(log); +}; + +module.exports = { + initPalworldBridge, + handlePalworldChat +};