const { Client, GatewayIntentBits, EmbedBuilder } = require('discord.js'); const axios = require('axios'); // Load configuration from environment variables or config.json const config = { token: process.env.DISCORD_TOKEN || require('./config.json').token, guildID: process.env.GUILD_ID || require('./config.json').guildID, channelID: process.env.CHANNEL_ID || require('./config.json').channelID, clientID: process.env.CLIENT_ID || require('./config.json').clientID, updatetime: parseInt(process.env.UPDATE_TIME) || require('./config.json').updatetime, backendUrl: process.env.BACKEND_URL || '', uptimeKumaUrl: process.env.UPTIME_KUMA_URL || '' }; const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent ] }); let monitorMessages = { 'Web Services': null, 'Infrastructure': null, 'Network': null }; client.once('ready', async () => { console.log('Bot is online!'); const channel = await client.channels.fetch(config.channelID); if (channel && channel.isTextBased()) { await clearChannel(channel); } else { console.error(`Unable to find text channel with ID ${config.channelID}`); } // Call the function to update messages immediately await updateMessages(); // Set interval to update messages every 30 seconds setInterval(updateMessages, config.updatetime * 1000); }); async function updateMessages() { try { const guild = await client.guilds.fetch(config.guildID); if (!guild) { console.error(`Unable to find guild with ID ${config.guildID}`); return; } const channel = await guild.channels.fetch(config.channelID); if (!channel || !channel.isTextBased()) { console.error(`Unable to find text channel with ID ${config.channelID}`); return; } console.log(`Fetching from backend: ${config.backendUrl}`); const response = await axios.get(config.backendUrl); const monitors = response.data; console.log('Backend response status:', response.status); console.log('Backend response data type:', typeof monitors); console.log('Backend response data:', JSON.stringify(monitors).substring(0, 500) + '...'); // Check if the backend returned an error if (monitors.error) { console.error('Backend API error:', monitors.message); return; } // Ensure monitors is an array if (!Array.isArray(monitors)) { console.error('Backend returned invalid data format:', typeof monitors); console.error('Expected array, got:', monitors); return; } console.log(`Fetched ${monitors.length} monitors from backend`); console.log('Monitor names:', monitors.map(m => m.monitor_name)); const webServicesMonitors = monitors.filter(monitor => [ 'Main Page', 'Pelican', 'Jellyfin', 'Proxmox', 'Jellyseerr', 'CPanel', 'WHMCS', 'Gitea', 'Nextcloud', 'Radarr', 'Sonarr', 'Prowlarr', 'Nginx Proxy Manager', 'Authentik', 'n8n', 'HA Proxy' ].includes(monitor.monitor_name)); const infrastructureMonitors = monitors.filter(monitor => [ 'a01.pve.hrs', 'a05.pve.hrs', 'a07.pve.hrs', 'a08.pve.hrs', 'a09.pve.hrs', '01.bw.hrs', '01.ga.hrs', '01.ha.hrs', '01.lh.hrs', '01.nc.hrs' ].includes(monitor.monitor_name)); const networkMonitors = monitors.filter(monitor => [ '01.sh.hrs', '01.rs.hrs', '01.pe.hrs', '01.pt.hrs', '01.rr.hrs', '01.wn.hrs', '02.pe.hrs', '01.cp.hrs', '02.cp.hrs', 'a06' ].includes(monitor.monitor_name)); console.log(`Web Services: ${webServicesMonitors.length}, Infrastructure: ${infrastructureMonitors.length}, Network: ${networkMonitors.length}`); await sendMonitorsMessage(channel, 'Web Services', webServicesMonitors); // await sendMonitorsMessage(channel, 'Infrastructure', infrastructureMonitors); //await sendMonitorsMessage(channel, 'Network', networkMonitors); // Send a test message if no monitors found if (webServicesMonitors.length === 0) { console.log('No monitors found, sending test message'); await channel.send('🔧 Bot is running but no monitors found. Check backend configuration.'); } } catch (error) { console.error('Error updating messages:', error); // If it's an axios error, log more details if (error.response) { console.error('Backend API error:', error.response.status, error.response.statusText); console.error('Response data:', error.response.data); } else if (error.request) { console.error('No response from backend API:', error.request); } else { console.error('Request setup error:', error.message); } } } async function sendMonitorsMessage(channel, category, monitors) { console.log(`Processing ${category}: ${monitors.length} monitors`); let description = monitors.map(monitor => { let statusEmoji = ''; switch (monitor.status) { case 0: statusEmoji = '🔴'; // Offline break; case 1: statusEmoji = '🟢'; // Online break; case 2: statusEmoji = '🟡'; // Warning break; case 3: statusEmoji = '🔵'; // Maintenance break; default: statusEmoji = '❓'; // Unknown } return `${statusEmoji} | ${monitor.monitor_name}`; }).join('\n'); console.log(`Generated description for ${category}: "${description}"`); // Ensure description is not empty (Discord.js validation requirement) if (!description || description.trim() === '') { description = `No ${category.toLowerCase()} monitors found.`; console.log(`Empty description, using fallback: "${description}"`); } let embed = new EmbedBuilder() .setTitle(`${category} Monitor`) .setColor('#0099ff') .setDescription(description) .setFooter({ text: `Last updated: ${new Date().toLocaleString()}` }) .setURL(config.uptimeKumaUrl); try { if (monitorMessages[category]) { const message = await channel.messages.fetch(monitorMessages[category]); if (message) { await message.edit({ embeds: [embed] }); console.log(`${new Date().toLocaleString()} | Updated ${category} monitors message`); } else { const newMessage = await channel.send({ embeds: [embed] }); monitorMessages[category] = newMessage.id; console.log(`${new Date().toLocaleString()} | Sent new ${category} monitors message`); } } else { const newMessage = await channel.send({ embeds: [embed] }); monitorMessages[category] = newMessage.id; console.log(`${new Date().toLocaleString()} | Sent ${category} monitors message`); } } catch (error) { console.error(`Failed to send/update ${category} monitors message:`, error); } } async function clearChannel(channel) { try { const fetchedMessages = await channel.messages.fetch(); await channel.bulkDelete(fetchedMessages); console.log('Cleared channel'); } catch (error) { console.error('Error clearing channel:', error); } } client.login(config.token).catch(error => { console.error('Error logging in:', error); });