diff --git a/src/helper.js b/src/helper.js index cd7bd278d8..dcebb68858 100644 --- a/src/helper.js +++ b/src/helper.js @@ -379,6 +379,16 @@ export function renderRaceTier(completeTiers) { return "●".repeat(completeTiers) + "○".repeat(incompleteTiers); } +/** + * returns a string with 5 dots "●" for completed tiers and "○" for incomplete tiers + * @param {number} completeTiers + * @returns {string} 5 dots + */ +export function renderAchievementTier(completeTiers) { + const incompleteTiers = Math.max(0, 5 - completeTiers); + return "●".repeat(completeTiers) + "○".repeat(incompleteTiers); +} + /** * checks whether a string should be proceeded by a or by an * @param {string} string @@ -533,6 +543,7 @@ export async function updateRank(uuid, db) { plusColor: null, socials: {}, achievements: {}, + achievementsOneTime: {}, claimed_items: {}, }; @@ -558,6 +569,10 @@ export async function updateRank(uuid, db) { rank.achievements = player.achievements; } + if (player?.achievementsOneTime != undefined) { + rank.achievementsOneTime = player.achievementsOneTime; + } + const claimable = { claimed_potato_talisman: "Potato Talisman", claimed_potato_basket: "Potato Basket", @@ -602,7 +617,7 @@ export async function getRank(uuid, db, cacheOnly = false) { hypixelPlayer = await _updateRank; } - hypixelPlayer ??= { achievements: {} }; + hypixelPlayer ??= { achievements: {}, achievementsOneTime: {} }; return hypixelPlayer; } diff --git a/src/lib.js b/src/lib.js index 51603daae2..75aa84acb2 100644 --- a/src/lib.js +++ b/src/lib.js @@ -1931,6 +1931,7 @@ export const getStats = async ( misc.auctions_sell = {}; misc.auctions_buy = {}; misc.claimed_items = {}; + misc.achievements = {}; if ("ender_crystals_destroyed" in userProfile.stats) { misc.dragons["ender_crystals_destroyed"] = userProfile.stats["ender_crystals_destroyed"]; @@ -1943,6 +1944,57 @@ export const getStats = async ( misc.claimed_items = hypixelProfile.claimed_items; } + const _tiered = { sum: 0, total: 0, completed: {}, uncompleted: {} }; + const _oneTime = { sum: 0, total: 0, completed: {}, uncompleted: {} }; + for await (const tmp of db.collection("achievements").find()) { + if (tmp?.tiered) { + _tiered.total += tmp.achievement.tiers.map((a) => a.points).reduce((a, b) => a + b, 0); + + let tier = 0; + for (const req in tmp.achievement.tiers) { + if (tmp.achievement.tiers[req].amount <= hypixelProfile.achievements["skyblock_" + tmp.id.toLowerCase()]) { + tier++; + _tiered.sum += Number(tmp.achievement.tiers[req].points); + } + } + + const achievement = { + name: tmp.achievement.name, + description: tmp.achievement.description.replaceAll("%s", "x"), + level: hypixelProfile.achievements["skyblock_" + tmp.id.toLowerCase()] || 0, + tier: tier, + }; + + if (tier >= 5) { + _tiered.completed["skyblock_" + tmp.id.toLowerCase()] = achievement; + } else { + _tiered.uncompleted["skyblock_" + tmp.id.toLowerCase()] = achievement; + } + } + + if (tmp?.one_time) { + _oneTime.total += tmp.achievement.points; + const achievement = { + name: tmp.achievement.name, + description: tmp.achievement.description, + reward: tmp.achievement.points, + }; + + if ( + hypixelProfile.achievementsOneTime && + hypixelProfile.achievementsOneTime.includes("skyblock_" + tmp.id.toLowerCase()) + ) { + _oneTime.completed["skyblock_" + tmp.id.toLowerCase()] = achievement; + _oneTime.sum += Number(tmp.achievement.points); + } else { + _oneTime.uncompleted["skyblock_" + tmp.id.toLowerCase()] = achievement; + } + } + } + + misc.achievements.tiered = _tiered; + misc.achievements.oneTime = _oneTime; + const burrows = [ "mythos_burrows_dug_next", "mythos_burrows_dug_combat", diff --git a/src/master.js b/src/master.js index d6bdbcb276..47d3ca9ff8 100644 --- a/src/master.js +++ b/src/master.js @@ -4,6 +4,7 @@ await import("./scripts/init-collections.js"); await Promise.all([ import("./scripts/cap-leaderboards.js"), import("./scripts/clear-favorite-cache.js"), + import("./scripts/update-achievements.js"), import("./scripts/update-bazaar.js"), import("./scripts/update-items.js"), import("./scripts/update-featured-profiles.js"), diff --git a/src/scripts/init-collections.js b/src/scripts/init-collections.js index df4c076cf5..e91e2f23e5 100644 --- a/src/scripts/init-collections.js +++ b/src/scripts/init-collections.js @@ -37,4 +37,6 @@ await Promise.all([ db.collection("profileCache").createIndex({ profile_id: 1 }, { unique: true }), db.collection("featuredProfiles").createIndex({ total: -1 }), + + db.collection("achievements").createIndex({ id: 1 }, { unique: true }), ]); diff --git a/src/scripts/update-achievements.js b/src/scripts/update-achievements.js new file mode 100644 index 0000000000..d907f82c5a --- /dev/null +++ b/src/scripts/update-achievements.js @@ -0,0 +1,42 @@ +import { db } from "../mongo.js"; +import axios from "axios"; +import "axios-debug-log"; + +const Hypixel = axios.create({ + baseURL: "https://api.hypixel.net/", +}); + +async function updateAchievements() { + try { + const response = await Hypixel.get("resources/achievements"); + + const achievements = []; + const { one_time, tiered } = response.data.achievements.skyblock; + + for (const achId in one_time) { + achievements.push({ + id: achId, + one_time: true, + achievement: one_time[achId], + }); + } + + for (const achId in tiered) { + achievements.push({ + id: achId, + tiered: true, + achievement: tiered[achId], + }); + } + + achievements.forEach(async (item) => { + await db.collection("achievements").updateOne({ id: item.id }, { $set: item }, { upsert: true }); + }); + } catch (e) { + console.error(e); + } + + setTimeout(updateAchievements, 1000 * 60 * 60 * 6); +} + +updateAchievements(); diff --git a/views/stats.ejs b/views/stats.ejs index 5fac4c9193..440c42fde4 100644 --- a/views/stats.ejs +++ b/views/stats.ejs @@ -2478,7 +2478,6 @@ const metaDescription = getMetaDescription() <% } %>

<% } %> - <% if ('uncategorized' in calculated.misc && Object.keys(calculated.misc.uncategorized).length > 0) { %>
Uncategorized

@@ -2488,6 +2487,59 @@ const metaDescription = getMetaDescription() <% } %>

<% } %> + <% if ('achievements' in calculated.misc) { %> + <% const list = calculated.misc.achievements; %> +
achievements
+

+ <% if('tiered' in list){ %> + ">Tiered AP: "><%= list.tiered.sum %> / <%= list.tiered.total %> (<%= Math.floor(list.tiered.sum / list.tiered.total * 100) %>%)
+ <% } %> + <% if('oneTime' in list){ %> + ">One Time AP: "><%= list.oneTime.sum %> / <%= list.oneTime.total %> (<%= Math.floor(list.oneTime.sum / list.oneTime.total * 100) %>%)
+ <% } %> +

+ +
+
+
Tiered
+
+ <% if('tiered' in list && 'uncompleted' in list.tiered && 'completed' in list.tiered){ %> + <% for(const key in list.tiered.uncompleted){ %> +
+
<%= list.tiered.uncompleted[key].name %> (<%= list.tiered.uncompleted[key].level %>)
+
<%= helper.renderAchievementTier(list.tiered.uncompleted[key].tier) %>
+
+ <% } %> + <% for(const key in list.tiered.completed){ %> +
+
<%= list.tiered.completed[key].name %> (<%= list.tiered.completed[key].level %>)
+
<%= helper.renderAchievementTier(list.tiered.completed[key].tier) %>
+
+ <% } %> + <% } %> +
+
+
+
One Time
+
+ <% if('oneTime' in list && 'uncompleted' in list.oneTime && 'completed' in list.oneTime){ %> + <% for(const key in list.oneTime.uncompleted){ %> +
+
<%= list.oneTime.uncompleted[key].name %>
+
 <%= list.oneTime.uncompleted[key].reward %>
+
+ <% } %> + <% for(const key in list.oneTime.completed){ %> +
+
<%= list.oneTime.completed[key].name %>
+
 <%= list.oneTime.completed[key].reward %>
+
+ <% } %> + <% } %> +
+
+
+ <% } %> <% } %>