const { Client, Events, GatewayIntentBits } = require("discord.js") const axios = require("axios") const fs = require("fs-extra") const path = require("path") const config = require("../config/config.json") const api = require("../config/api.json") const { channel_id, minecraft_version, modrinth_mods } = config const { token } = api const cacheFile = path.join(__dirname, "../cache/mod_versions.json") const client = new Client({ intents: [GatewayIntentBits.Guilds] }) 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 GetCompatibleModVersion = async (slug, targetMinecraftVersion) => { try { const res = await axios.get(`https://api.modrinth.com/v2/project/${slug}/version`) const fabricVersions = res.data.filter(v => v.loaders.includes("fabric")) if (fabricVersions.length === 0) return null const compatible = fabricVersions.find(v => v.game_versions.includes(targetMinecraftVersion)) const latest = fabricVersions.sort((a, b) => new Date(b.date_published) - new Date(a.date_published) )[0] return { latest, compatible: compatible || null } } catch (err) { console.error(`[Modrinth] Error fetching ${slug}:`, err.message) return null } } const CheckModUpdates = async () => { console.log("[Check] Checking for mod compatibility...") const cache = await LoadCache() const newCache = { ...cache } const compatibleReleased = [] const waitingUpdate = [] const newlyCompatible = [] for (const slug of modrinth_mods) { const modData = await GetCompatibleModVersion(slug, minecraft_version) if (!modData) continue const { latest, compatible } = modData const latestId = latest.id const cachedId = cache[slug] if (compatible) { compatibleReleased.push({ slug }) newCache[slug] = latestId if (!cachedId || cachedId !== latestId) { newlyCompatible.push({ slug }) console.log(`[Update] ${slug} is now compatible with Minecraft ${minecraft_version}`) } } else { waitingUpdate.push({ slug }) console.log(`[Check] ${slug} is waiting for a compatible version`) } } await SaveCache(newCache) return { compatibleReleased, waitingUpdate, newlyCompatible } } const SendUpdateMessages = async (channel, compatibleReleased, waitingUpdate, newlyCompatible) => { if (!compatibleReleased.length && !waitingUpdate.length) { console.log("[Update]: No mods to report.") return } let message = `**Mod Compatibility Check — Minecraft ${minecraft_version}**\n\n` if (newlyCompatible.length > 0) { message += `**🎉 Mods Recently Updated:**\n` for (const { slug } of newlyCompatible) message += `✅ ${slug}\n` message += `\n` } const stillCompatible = compatibleReleased.filter( m => !newlyCompatible.find(n => n.slug === m.slug) ) if (stillCompatible.length > 0) { message += `**🟢 Mods Compatible:**\n` for (const { slug } of stillCompatible) message += `✅ ${slug}\n` message += `\n` } if (waitingUpdate.length > 0) { message += `**⏳ Mods Waiting on Update:**\n` for (const { slug } of waitingUpdate) message += `❌ ${slug}\n` message += `\n` } await channel.send({ content: message, allowedMentions: { parse: [] }, flags: 4 }) console.log(`[Discord] Sent update summary (${newlyCompatible.length} updated, ${stillCompatible.length} compatible, ${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 { compatibleReleased, waitingUpdate, newlyCompatible } = await CheckModUpdates() await SendUpdateMessages(channel, compatibleReleased, waitingUpdate, newlyCompatible) setInterval(async () => { const { compatibleReleased, waitingUpdate, newlyCompatible } = await CheckModUpdates() await SendUpdateMessages(channel, compatibleReleased, waitingUpdate, newlyCompatible) }, 60 * 60 * 1000) }) client.login(token)