diff --git a/src/pterodactyl/displayer.js b/src/pterodactyl/displayer.js index 0ef7132..82f9406 100644 --- a/src/pterodactyl/displayer.js +++ b/src/pterodactyl/displayer.js @@ -1,4 +1,6 @@ const { EmbedBuilder, ActionRowBuilder, StringSelectMenuBuilder } = require('discord.js'); +const fs = require('node:fs'); +const path = require('node:path'); const PINBOARD_SELECT_CUSTOM_ID = 'pinboard-image-select'; const selectedImageIndexByMessage = new Map(); @@ -18,12 +20,72 @@ const toCacheBustedUrl = (url) => { return `${url}${separator}cb=${getCacheBustValue()}`; }; -const parsePinboardImageUrls = () => { - const raw = process.env.PINBOARD_IMAGE_URLS || ''; - return raw +const readPinboardRawFromEnvFile = () => { + try { + const envPath = path.join(process.cwd(), '.env'); + if (!fs.existsSync(envPath)) { + return ''; + } + + const content = fs.readFileSync(envPath, 'utf8'); + const marker = 'PINBOARD_IMAGE_URLS='; + const start = content.indexOf(marker); + + if (start === -1) { + return ''; + } + + const afterMarkerIndex = start + marker.length; + const rest = content.slice(afterMarkerIndex); + const nextVarMatch = rest.match(/\n[A-Z0-9_]+=/); + const end = nextVarMatch ? afterMarkerIndex + nextVarMatch.index : content.length; + return content.slice(afterMarkerIndex, end).trim(); + } catch (error) { + return ''; + } +}; + +const parsePinboardImageItems = () => { + let raw = process.env.PINBOARD_IMAGE_URLS || ''; + + // Support multiline object-like config when dotenv only captures the first line. + if (!raw || raw.trim() === '{') { + raw = readPinboardRawFromEnvFile(); + } + + if (!raw) { + return []; + } + + const items = []; + const pairRegex = /name\s*:\s*([^,\n]+?)\s*,\s*url\s*:\s*(https?:\/\/[^\s,}\]]+)/gim; + + let match = pairRegex.exec(raw); + while (match) { + const name = match[1].trim().replace(/^['"]|['"]$/g, ''); + const url = match[2].trim().replace(/^['"]|['"]$/g, ''); + + if (name && /^https?:\/\//i.test(url)) { + items.push({ name, url }); + } + + match = pairRegex.exec(raw); + } + + if (items.length > 0) { + return items; + } + + // Fallback legacy: comma-separated URLs only + const urls = raw .split(',') .map(url => url.trim()) .filter(url => /^https?:\/\//i.test(url)); + + return urls.map((url, index) => ({ + name: `Image ${index + 1}`, + url + })); }; const clampIndex = (index, length) => { @@ -33,18 +95,19 @@ const clampIndex = (index, length) => { return index; }; -const buildSelectedImageEmbed = (url, index, total) => ( +const buildSelectedImageEmbed = (item, index, total) => ( new EmbedBuilder() .setColor('#f59e0b') - .setTitle(`Image selectionnee (${index + 1}/${total})`) - .setImage(toCacheBustedUrl(url)) + .setTitle(item.name) + .setDescription(`Image ${index + 1}/${total}`) + .setImage(toCacheBustedUrl(item.url)) ); -const buildImageSelectorRow = (urls, selectedIndex) => { - const options = urls.slice(0, 25).map((url, index) => ({ - label: `Image ${index + 1}`, +const buildImageSelectorRow = (items, selectedIndex) => { + const options = items.slice(0, 25).map((item, index) => ({ + label: item.name.length > 100 ? `${item.name.slice(0, 97)}...` : item.name, value: String(index), - description: url.length > 95 ? `${url.slice(0, 92)}...` : url, + description: item.url.length > 95 ? `${item.url.slice(0, 92)}...` : item.url, default: index === selectedIndex })); @@ -100,17 +163,17 @@ const buildPanelEmbed = (status) => { const buildPanelPayload = (status, messageId) => { const panelEmbed = buildPanelEmbed(status); - const pinboardUrls = parsePinboardImageUrls(); + const pinboardItems = parsePinboardImageItems().slice(0, 25); - if (pinboardUrls.length === 0) { + if (pinboardItems.length === 0) { return { content: '', embeds: [panelEmbed], components: [] }; } - const currentIndex = clampIndex(selectedImageIndexByMessage.get(messageId) ?? 0, pinboardUrls.length); + const currentIndex = clampIndex(selectedImageIndexByMessage.get(messageId) ?? 0, pinboardItems.length); selectedImageIndexByMessage.set(messageId, currentIndex); - const selectedImageEmbed = buildSelectedImageEmbed(pinboardUrls[currentIndex], currentIndex, pinboardUrls.length); - const selectorRow = buildImageSelectorRow(pinboardUrls, currentIndex); + const selectedImageEmbed = buildSelectedImageEmbed(pinboardItems[currentIndex], currentIndex, pinboardItems.length); + const selectorRow = buildImageSelectorRow(pinboardItems, currentIndex); return { content: '', @@ -173,15 +236,15 @@ const handlePinboardSelection = async (interaction) => { } try { - const pinboardUrls = parsePinboardImageUrls(); + const pinboardItems = parsePinboardImageItems().slice(0, 25); - if (pinboardUrls.length === 0) { + if (pinboardItems.length === 0) { await interaction.reply({ content: 'Aucune image configuree dans PINBOARD_IMAGE_URLS.', ephemeral: true }); return true; } const selectedRaw = Number.parseInt(interaction.values?.[0] || '0', 10); - const selectedIndex = clampIndex(selectedRaw, pinboardUrls.length); + const selectedIndex = clampIndex(selectedRaw, pinboardItems.length); selectedImageIndexByMessage.set(interaction.message.id, selectedIndex); const status = await getMinecraftStatus();