From 9010ed1f600413465468b824036cff471d3972c1 Mon Sep 17 00:00:00 2001 From: hachem Date: Sun, 23 Nov 2025 05:01:11 +0100 Subject: [add]: i spent way too much time on this stupid bot omg --- bot/app.js | 306 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 155 insertions(+), 151 deletions(-) (limited to 'bot/app.js') diff --git a/bot/app.js b/bot/app.js index d0d5084..3fc9134 100644 --- a/bot/app.js +++ b/bot/app.js @@ -1,8 +1,8 @@ const { - Client, - Events, - GatewayIntentBits + Client, + Events, + GatewayIntentBits } = require("discord.js") const axios = require("axios") @@ -14,190 +14,194 @@ const api = require("../config/api.json") const { - channel_id, - minecraft_version, - modrinth_mods + channel_id, + check_interval_minutes, + minecraft_version, + fabric_loader_version, + loader_meta_version, + modrinth_mods } = config -const -{ - token -} = api +const { token } = api const cacheFile = path.join(__dirname, "../cache/mod_versions.json") - const client = new Client ({ - intents: [GatewayIntentBits.Guilds] + intents: + [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages + ] }) 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 {} + try + { + if (await fs.pathExists(cacheFile)) + return JSON.parse(await fs.readFile(cacheFile, "utf8")) + } + catch (err) + { + console.error("[Cache] Failed to load:", 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) - } + try + { + await fs.ensureDir(path.dirname(cacheFile)) + await fs.writeFile(cacheFile, JSON.stringify(cache, null, 2)) + } + catch (err) + { + console.error("[Cache] Failed to save:", err) + } } -const GetModrinthData = async slug => +const GetModVersions = async slug => { - try - { - const r = await axios.get(`https://api.modrinth.com/v2/project/${slug}/version`) - return r.data - } - catch (err) - { - console.error(`[Modrinth] Failed for ${slug}:`, err.message) - return null - } + try + { + const response = await axios.get(`https://api.modrinth.com/v2/project/${slug}/version`) + return response.data + } + catch (err) + { + console.error(`[Modrinth] Failed for ${slug}:`, err.message) + return null + } } const CheckMods = async () => { - const cache = await LoadCache() - const newCache = { ...cache } - - const updatedNotCompatible = [] - const newlyCompatible = [] - const compatibleUpdated = [] - - for (const slug of modrinth_mods) - { - const versions = await GetModrinthData(slug) - if (!versions) - continue - - const fabric = versions.filter(v => v.loaders.includes("fabric")) - if (!fabric.length) - continue - - const latest = fabric.sort((a, b) => - new Date(b.date_published) - new Date(a.date_published) - )[0] - - const latestId = latest.id - const compatible = latest.game_versions.includes(minecraft_version) - const cachedId = cache[slug] + const cache = await LoadCache() + const newCache = { ...cache } - if (!cachedId) - newCache[slug] = latestId + const noCompatible = [] + const newlyCompatible = [] + const stillCompatibleUpdate = [] - if (latestId === cachedId) - continue - - const previouslyCompatible = fabric.some(v => - v.id === cachedId && v.game_versions.includes(minecraft_version) - ) + for (const slug of modrinth_mods) + { + const versions = await GetModVersions(slug) + if (!versions) + continue + + const latest = versions.sort((a, b) => new Date(b.date_published) - new Date(a.date_published))[0] + const compatible = versions.find(v => v.game_versions.includes(minecraft_version)) || null + + const cached = cache[slug] || + { + compatible: null, + latest: null + } + + newCache[slug] = + { + compatible: compatible ? compatible.id : null, + latest: latest.id + } + + if (!compatible) + { + noCompatible.push(slug) + continue + } + + if (!cached.compatible && compatible) + { + newlyCompatible.push(slug) + continue + } + + if (latest.id !== cached.latest) + stillCompatibleUpdate.push(slug) + } + + await SaveCache(newCache) + + return { noCompatible, newlyCompatible, stillCompatibleUpdate } +} - if (!compatible) - updatedNotCompatible.push(slug) - else if (compatible && !previouslyCompatible) - newlyCompatible.push(slug) - else if (compatible && previouslyCompatible) - compatibleUpdated.push(slug) +const SendUpdate = async (channel, noCompatible, newlyCompatible, stillCompatibleUpdate) => +{ + if (!noCompatible.length && !newlyCompatible.length && !stillCompatibleUpdate.length) + return - newCache[slug] = latestId - } + let msg = `**Modrinth Compatibility — ${minecraft_version}**\n\n` - await SaveCache(newCache) - return { updatedNotCompatible, newlyCompatible, compatibleUpdated } -} + if (noCompatible.length) + { + msg += `**❌ No compatible version available:**\n` + for (const slug of noCompatible) + msg += `❌ ${slug}\n` + msg += `\n` + } -const SendUpdate = async (channel, updatedNotCompatible, newlyCompatible, compatibleUpdated) => -{ - if (!updatedNotCompatible.length && !newlyCompatible.length && !compatibleUpdated.length) - return - - let msg = `**Modrinth Update Check — ${minecraft_version}**\n\n` - - if (updatedNotCompatible.length) - { - msg += `**❗ Updated but NOT compatible:**\n` - for (const s of updatedNotCompatible) - msg += `❗ ${s}\n` - msg += `\n` - } - - if (newlyCompatible.length) - { - msg += `**🎉 NOW Compatible:**\n` - for (const s of newlyCompatible) - msg += `🎉 ${s}\n` - msg += `\n` - } - - if (compatibleUpdated.length) - { - msg += `**🟢 Updated (still compatible):**\n` - for (const s of compatibleUpdated) - msg += `🟢 ${s}\n` - msg += `\n` - } - - await channel.send - ({ - content: msg, - allowedMentions: + if (newlyCompatible.length) { - parse: [] - }, - flags: 4 - }) + msg += `**🎉 Now Compatible:**\n` + for (const slug of newlyCompatible) + msg += `🎉 ${slug}\n` + msg += `\n` + } - console.log("[Discord] Sent update.") + if (stillCompatibleUpdate.length) + { + msg += `**🟢 Updated (still compatible):**\n` + for (const slug of stillCompatibleUpdate) + msg += `🟢 ${slug}\n` + msg += `\n` + } + + await channel.send + ({ + content: msg, + allowedMentions: { parse: [] } + }) + + console.log("[Discord] Update message sent.") } client.once(Events.ClientReady, async c => { - console.log(`[OK] Logged in as ${c.user.tag}`) - - const channel = await client.channels.fetch(channel_id).catch(() => null) - if (!channel) - { - console.error("Invalid channel_id:", channel_id) - return - } - - const - { - updatedNotCompatible, - newlyCompatible, - compatibleUpdated - } = await CheckMods() - - await SendUpdate(channel, updatedNotCompatible, newlyCompatible, compatibleUpdated) - - setInterval(async () => - { + console.log(`[OK] Logged in as ${c.user.tag}`) + + let channel = null + try + { + channel = await client.channels.fetch(channel_id) + console.log("Fetched channel:", channel.name, "type:", channel.type) + } + catch + { + console.error("❌ Failed to fetch channel:", channel_id) + return + } + const { - updatedNotCompatible, - newlyCompatible, - compatibleUpdated + noCompatible, + newlyCompatible, + stillCompatibleUpdate } = await CheckMods() - await SendUpdate(channel, updatedNotCompatible, newlyCompatible, compatibleUpdated) - }, 60 * 60 * 1000) + await SendUpdate(channel, noCompatible, newlyCompatible, stillCompatibleUpdate) + + setInterval(async () => + { + const + { + noCompatible, + newlyCompatible, + stillCompatibleUpdate + } = await CheckMods() + + await SendUpdate(channel, noCompatible, newlyCompatible, stillCompatibleUpdate) + }, check_interval_minutes * 60 * 1000) }) -client.login(token) +client.login(token) \ No newline at end of file -- cgit v1.3