Skip to content
This repository was archived by the owner on Aug 12, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/cachetimes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import credentials from "./credentials.js";

/**
* Check if a resource is expired based on the cache time
* @param {number} unixMs Unix timestamp in milliseconds
* @param {number} cacheTime The cache time in seconds
* @returns {boolean} Whether the cache is expired
*/
export function isCacheExpired(unixMs, cacheTime) {
return Date.now() - unixMs > cacheTime * 1000;
}

/**
* Check if a profile cache is expired based on the configured cache time
* @param {number} unixMs Unix timestamp in milliseconds
* @returns {boolean} Whether the cache is expired
*/
export function isProfileCacheExpired(unixMs) {
return isCacheExpired(unixMs, credentials.cacheSeconds.profiles);
}

/**
* Check if a museum cache is expired based on the configured cache time
* @param {number} unixMs Unix timestamp in milliseconds
* @returns {boolean} Whether the cache is expired
*/
export function isMuseumCacheExpired(unixMs) {
return isCacheExpired(unixMs, credentials.cacheSeconds.museum);
}

/**
* Check if a guild cache is expired based on the configured cache time
* @param {number} unixMs Unix timestamp in milliseconds
* @returns {boolean} Whether the cache is expired
*/
export function isGuildCacheExpired(unixMs) {
return isCacheExpired(unixMs, credentials.cacheSeconds.guild);
}

/**
* Check if a bingo profile cache is expired based on the configured cache time
* @param {number} unixMs Unix timestamp in milliseconds
* @returns {boolean} Whether the cache is expired
*/
export function isBingoProfileCacheExpired(unixMs) {
return isCacheExpired(unixMs, credentials.cacheSeconds.bingoProfiles);
}
31 changes: 31 additions & 0 deletions src/credentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ const defaultCredentials = {
get session_secret() {
return randomBytes(32).toString("hex");
},
cacheSeconds: {
profiles: 60 * 5, // 5 minutes
bingoProfile: 60 * 5, // 5 minutes
museum: 60 * 30, // 30 minutes
guild: 60 * 60 * 24, // 24 hours
},
};

/**
Expand Down Expand Up @@ -50,6 +56,15 @@ for (const key in defaultCredentials) {
CREDENTIALS[key] = defaultCredentials[key];
hasBeenModified = true;
}

if (typeof defaultCredentials[key] === "object") {
for (const subKey in defaultCredentials[key]) {
if (CREDENTIALS[key][subKey] == undefined) {
CREDENTIALS[key][subKey] = defaultCredentials[key][subKey];
hasBeenModified = true;
}
}
}
}

if (hasBeenModified) {
Expand All @@ -72,4 +87,20 @@ if (process.env.DISCORD_WEBHOOK) {
CREDENTIALS.discord_webhook = process.env.DISCORD_WEBHOOK;
}

if (process.env.CACHE_PROFILES && isFinite(parseInt(process.env.CACHE_PROFILES))) {
CREDENTIALS.cacheSeconds.profiles = parseInt(process.env.CACHE_PROFILES);
}

if (process.env.CACHE_MUSEUM && isFinite(parseInt(process.env.CACHE_MUSEUM))) {
CREDENTIALS.cacheSeconds.museum = parseInt(process.env.CACHE_MUSEUM);
}

if (process.env.CACHE_GUILD && isFinite(parseInt(process.env.CACHE_GUILD))) {
CREDENTIALS.cacheSeconds.guild = parseInt(process.env.CACHE_GUILD);
}

if (process.env.CACHE_BINGO_PROFILE && isFinite(parseInt(process.env.CACHE_BINGO_PROFILE))) {
CREDENTIALS.cacheSeconds.bingoProfile = parseInt(process.env.CACHE_BINGO_PROFILE);
}

export default CREDENTIALS;
22 changes: 6 additions & 16 deletions src/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import axios from "axios";

import * as constants from "./constants.js";
import credentials from "./credentials.js";
import * as cacheTimes from "./cachetimes.js";
import * as helper from "./helper.js";
import * as stats from "./stats.js";
import { SkyCryptError } from "./constants/error.js";
Expand Down Expand Up @@ -255,8 +256,6 @@ export async function getProfile(

let profileObject = await db.collection("profileStore").findOne({ uuid: sanitize(paramPlayer) });

let lastCachedSave = 0;

if (profileObject) {
const profileData = db
.collection("profileCache")
Expand All @@ -276,13 +275,9 @@ export async function getProfile(

let response = null;

lastCachedSave = Math.max(profileObject.last_update, Date.now() || 0);
const lastCachedSave = profileObject.last_update;

if (
!options.cacheOnly &&
((Date.now() - lastCachedSave > 190 * 1000 && Date.now() - lastCachedSave < 300 * 1000) ||
Date.now() - profileObject.last_update >= 300 * 1000)
) {
if (!options.cacheOnly && (cacheTimes.isProfileCacheExpired(lastCachedSave) || lastCachedSave === 0)) {
try {
profileObject.last_update = Date.now();
response = await retry(
Expand Down Expand Up @@ -504,12 +499,7 @@ export async function getBingoProfile(
};

const lastCachedSave = profileData.last_save ?? 0;
if (
(!options.cacheOnly &&
((Date.now() - lastCachedSave > 190 * 1000 && Date.now() - lastCachedSave < 300 * 1000) ||
Date.now() - profileData.last_save >= 300 * 1000)) ||
lastCachedSave === 0
) {
if (!options.cacheOnly && (cacheTimes.isBingoProfileCacheExpired(lastCachedSave) || lastCachedSave === 0)) {
try {
const response = await retry(
async () => {
Expand Down Expand Up @@ -565,7 +555,7 @@ export async function getMuseum(
}

let museumData = await db.collection("museumCache").findOne({ profile_id: profileID });
if (!options.cacheOnly && (museumData == undefined || museumData.last_save < Date.now() - 1000 * 60 * 30)) {
if (!options.cacheOnly && (museumData == undefined || cacheTimes.isMuseumCacheExpired(museumData.last_save))) {
try {
const params = {
key: credentials.hypixel_api_key,
Expand Down Expand Up @@ -616,7 +606,7 @@ export async function getGuild(
const timeStarted = Date.now();

let output = await db.collection("guildCache").findOne({ uuid: paramPlayer });
if (!options.cacheOnly && (!output || output.last_updated < Date.now() - 1000 * 60 * 60 * 24)) {
if (!options.cacheOnly && (!output || cacheTimes.isGuildCacheExpired(output.last_updated))) {
try {
const params = {
key: credentials.hypixel_api_key,
Expand Down
63 changes: 33 additions & 30 deletions views/stats.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function itemIcon(item, classes) { %>
icon-<%= item.id %>_<%= item.Damage %>
">
</div>
<% }
<% }

function jerriefy(rank) {
if (extra.isFoolsDay) {
Expand Down Expand Up @@ -380,7 +380,7 @@ const metaDescription = getMetaDescription()
<% for (let error in calculated.errors) { %>
<% console.log(calculated.errors[error]) %>
<%- calculated.errors[error] %>

<br>
<% } %>
<br>
Expand Down Expand Up @@ -438,7 +438,7 @@ const metaDescription = getMetaDescription()
<% for (let profile_id in calculated.profiles) {
const _profile = calculated.profiles[profile_id]; %>
<li>
<a class="goto" href="/stats/<%= calculated.uuid %>/<%= _profile.profile_id %><%= Object.keys(req.query).length > 0 ? '?' + new URLSearchParams(req.query).toString() : '' %>">
<a class="goto" href="/stats/<%= calculated.uuid %>/<%= _profile.profile_id %><%= Object.keys(req.query).length > 0 ? '?' + new URLSearchParams(req.query).toString() : '' %>">
<span class="profile-name"><%= _profile.cute_name %></span>

<% if (_profile.game_mode == 'ironman') { %>
Expand Down Expand Up @@ -468,40 +468,43 @@ const metaDescription = getMetaDescription()

<!-- Placnke Button-->
<a href="https://plancke.io/hypixel/player/stats/<%= calculated.display_name %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link">Plancke</a>


<!-- Elite Button-->
<a href="https://elitebot.dev/@<%= calculated.display_name %>/<%= calculated.profile.profile_id %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link">Elite</a>

<div id="additional_socials">
<!-- Copy UUID Button -->
<button data-copy-text="<%= calculated.uuid %>" class="copy-text additional-player-stat">Copy UUID</button>

<% if ('social' in calculated && Object.keys(calculated.social).length > 0) { %>
<!-- Discord Button -->
<% if ('DISCORD' in calculated.social) { %>
<button data-copy-text="<%= calculated.social.DISCORD %>" class="additional-player-stat copy-text external-discord external-icon"><%= calculated.social.DISCORD %></button>
<% if ('DISCORD' in calculated.social) { %>
<button data-copy-text="<%= calculated.social.DISCORD %>" class="additional-player-stat copy-text external-discord external-icon"><%= calculated.social.DISCORD %></button>
<% } %>

<!-- Twitter Button -->
<% if ('TWITTER' in calculated.social) { %>
<a data-tippy-content="Twitter" href="<%= calculated.social.TWITTER %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link external-icon external-twitter"></a>
<% if ('TWITTER' in calculated.social) { %>
<a data-tippy-content="Twitter" href="<%= calculated.social.TWITTER %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link external-icon external-twitter"></a>
<% } %>

<!-- Youtube Button -->
<% if ('YOUTUBE' in calculated.social) { %>
<a data-tippy-content="YouTube" href="<%= calculated.social.YOUTUBE %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link external-icon external-youtube"></a>
<% if ('YOUTUBE' in calculated.social) { %>
<a data-tippy-content="YouTube" href="<%= calculated.social.YOUTUBE %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link external-icon external-youtube"></a>
<% } %>

<!-- Instagram Button -->
<% if ('INSTAGRAM' in calculated.social) { %>
<a data-tippy-content="Instagram" href="<%= calculated.social.INSTAGRAM %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link external-icon external-instagram"></a>
<% if ('INSTAGRAM' in calculated.social) { %>
<a data-tippy-content="Instagram" href="<%= calculated.social.INSTAGRAM %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link external-icon external-instagram"></a>
<% } %>

<!-- Twitch Button -->
<% if ('TWITCH' in calculated.social) { %>
<a data-tippy-content="Twitch" href="<%= calculated.social.TWITCH %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link external-icon external-twitch"></a>
<% if ('TWITCH' in calculated.social) { %>
<a data-tippy-content="Twitch" href="<%= calculated.social.TWITCH %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link external-icon external-twitch"></a>
<% } %>

<!-- Hypixel Button -->
<% if ('HYPIXEL' in calculated.social) { %>
<a data-tippy-content="Hypixel Forums" href="<%= calculated.social.HYPIXEL %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link external-icon external-hypixel"></a>
<% if ('HYPIXEL' in calculated.social) { %>
<a data-tippy-content="Hypixel Forums" href="<%= calculated.social.HYPIXEL %>" target="_blank" rel="noreferrer" class="additional-player-stat external-link external-icon external-hypixel"></a>
<% } %>

<button class="additional-player-stat" id="reveal_socials" aria-label="reveal socials"></button>
Expand Down Expand Up @@ -572,27 +575,27 @@ const metaDescription = getMetaDescription()
<% } %>

<% if (calculated.profile.game_mode == 'ironman') { %>
<% if (notAvailable.length > 0) { %>
<br>
<% if (notAvailable.length > 0) { %>
<br>
<% } %>

This is an <strong>Ironman</strong> profile. The player cannot use the auction house, bazaar, or trade with other players.
<% } %>


<% if (calculated.profile.game_mode == 'bingo') { %>
<% if (notAvailable.length > 0) { %>
<br>
<% if (notAvailable.length > 0) { %>
<br>
<% } %>

This is a <strong>Bingo</strong> profile. The player cannot spend gems, use the auction house, bazaar or trade with other players.
<% } %>

<% if (calculated.profile.game_mode == 'island') { %>
<% if (notAvailable.length > 0) { %>
<% if (notAvailable.length > 0) { %>
<br>
<% } %>

This is a <strong>Stranded</strong> profile. The player cannot leave their skyblock island or trade with other players.
<% } %>
</div>
Expand Down Expand Up @@ -630,22 +633,22 @@ const metaDescription = getMetaDescription()
<!-- Inventories (Inventory, Storage, Ender Chest, Vault, Accessories, Pots, Fish, Quiver) -->
<%- include('./sections/stats/items/inventory.ejs', { getRarityUpgradeClass, rarityOrder, isEnchanted }); %>
<% } %>

<!-- Skills -->
<%- include('./sections/stats/skills.ejs', { getRarityUpgradeClass, rarityOrder, isEnchanted }); %>

<!-- Dungeons -->
<% if (calculated.dungeons !== undefined) { %>
<%- include('./sections/stats/dungeons.ejs', { skillItems }); %>
<% } %>
<% } %>

<!-- Slayer -->
<% if (calculated.slayer !== undefined) { %>
<%- include('./sections/stats/slayer.ejs', {}); %>
<% } %>

<!-- Minions -->
<% if (calculated.minions.unlockedTiers > 0) { %>
<% if (calculated.minions.unlockedTiers > 0) { %>
<%- include('./sections/stats/minions.ejs', { skillItems }); %>
<% } %>

Expand All @@ -659,7 +662,7 @@ const metaDescription = getMetaDescription()
<% } %>

<!-- Crimson Isle -->
<% if (calculated.crimson_isle !== undefined) { %>
<% if (calculated.crimson_isle !== undefined) { %>
<%- include('./sections/stats/crimson_isle.ejs', {}); %>
<% } %>

Expand All @@ -672,8 +675,8 @@ const metaDescription = getMetaDescription()
<% if (calculated.profile.game_mode === "bingo" && calculated.bingo !== undefined) { %>
<%- include('./sections/stats/bingo.ejs', {}); %>
<% } %>
<!-- Miscellaneous -->

<!-- Miscellaneous -->
<% if (calculated.misc !== undefined) { %>
<%- include('./sections/stats/misc.ejs', { rarityOrder }); %>
<% } %>
Expand Down