From cc3dfce7dfbbeda9934892be631decb79a26dd28 Mon Sep 17 00:00:00 2001 From: hachem Date: Sun, 12 Oct 2025 15:45:04 +0200 Subject: [add]: updater --- bot/app.js | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 bot/app.js (limited to 'bot/app.js') diff --git a/bot/app.js b/bot/app.js new file mode 100644 index 0000000..d25b3b6 --- /dev/null +++ b/bot/app.js @@ -0,0 +1,161 @@ +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) -- cgit v1.3