const { Client, Events, GatewayIntentBits } = require("discord.js") const axios = require("axios") const fs = require("fs-extra") const path = require("path") const config = require("../config.json") const { token, channel_id, minecraft_version, modrinth_mods, } = config const cacheFile = path.join(__dirname, "../cache/mod_versions.json") const client = new Client({ intents: [GatewayIntentBits.Guilds] }) const strictVersionMatch = true const LoadCache = async () => { try { if (await fs.pathExists(cacheFile)) return JSON.parse(await fs.readFile(cacheFile, "utf8")) } catch (err) { console.error("[Cache] Failed to read cache:", err) } return {} } const SaveCache = async (cache) => { try { await fs.ensureDir(path.dirname(cacheFile)) await fs.writeFile(cacheFile, JSON.stringify(cache, null, 2)) } catch (err) { console.error("[Cache] Failed to save cache:", err) } } const GetLatestFabricVersion = async (slug) => { try { const res = await axios.get(`https://api.modrinth.com/v2/project/${slug}/version`) const versions = res.data.filter(v => v.loaders.includes("fabric")) if (versions.length === 0) return null return versions.sort( (a, b) => new Date(b.date_published) - new Date(a.date_published) )[0] } catch (err) { console.error(`[Modrinth] Error fetching ${slug}:`, err.message) return null } } const IsVersionCompatible = (modVersionList, targetVersion) => { if (strictVersionMatch) return modVersionList.includes(targetVersion) else return modVersionList.some(v => targetVersion.startsWith(v)) } const CheckModUpdates = async () => { console.log("[Check] Checking for mod updates...") const cache = await LoadCache() const newCache = { ...cache } const updates = [] const upToDate = [] const waitingUpdate = [] for (const slug of modrinth_mods) { const latest = await GetLatestFabricVersion(slug) if (!latest) continue const compatible = IsVersionCompatible(latest.game_versions, minecraft_version) const latestId = latest.id const cachedId = cache[slug] if (compatible) { if (cachedId !== latestId) { newCache[slug] = latestId updates.push({ slug, version: latest.version_number, url: `https://modrinth.com/mod/${slug}/version/${latestId}`, }) } else { upToDate.push({ slug, version: latest.version_number, url: `https://modrinth.com/mod/${slug}/version/${latestId}`, }) console.log(`[Check] ${slug} is up to date.`) } } else { waitingUpdate.push({ slug, version: latest.version_number, url: `https://modrinth.com/mod/${slug}/version/${latestId}`, }) console.log(`[Check] ${slug} waiting for update — not compatible with ${minecraft_version}`) } } await SaveCache(newCache) return { updates, upToDate, waitingUpdate } } const SendUpdateMessages = async (channel, updates, upToDate, waitingUpdate) => { if (updates.length === 0) { console.log("[Update]: No new updates."); return; } let message = "" message += `**Mod Update Check — Minecraft ${minecraft_version}**\n\n` if (updates.length > 0) { message += `**🟢 Updated Mods:**\n` for (const { slug, url } of updates) message += `✅ [${slug}](${url}) updated\n` message += `\n` } if (upToDate.lengt > 0) { message += `**⚪ Up-to-Date Mods:**\n` for (const { slug, url } of upToDate) message += `✅ [${slug}](${url})\n` message += `\n` } if (waitingUpdate.length > 0) { message += `**⏳ Mods Waiting for Update:**\n` for (const { slug, url } of waitingUpdate) message += `❌ [${slug}](${url})\n` } await channel.send( { content: message, allowedMentions: { parse: [] }, flags: 4 }) console.log(`[Discord] Sent update summary (${updates.length} updated, ${upToDate.length} up-to-date, ${waitingUpdate.length} waiting).`) } client.once(Events.ClientReady, async readyClient => { console.log(`[OK] Logged in as ${readyClient.user.tag}`) const channel = await client.channels.fetch(channel_id).catch(() => null) if (!channel) { console.error("❌ Could not find channel ID:", channel_id) return } const { updates, upToDate, waitingUpdate } = await CheckModUpdates() await SendUpdateMessages(channel, updates, upToDate, waitingUpdate) // ⏰ Check every hour (3600000 ms) setInterval(async () => { const { updates, upToDate, waitingUpdate } = await CheckModUpdates() await SendUpdateMessages(channel, updates, upToDate, waitingUpdate) }, 60 * 60 * 1000) }) client.login(token)