diff options
Diffstat (limited to 'build/database.js')
-rw-r--r-- | build/database.js | 184 |
1 files changed, 169 insertions, 15 deletions
diff --git a/build/database.js b/build/database.js index be193e9..600b21d 100644 --- a/build/database.js +++ b/build/database.js @@ -25,7 +25,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.queueUpdateDatabaseMember = exports.updateDatabaseMember = exports.fetchMemberLeaderboardSpots = exports.fetchMemberLeaderboard = exports.fetchAllMemberLeaderboardAttributes = exports.fetchSlayerLeaderboards = exports.fetchAllLeaderboardsCategorized = void 0; +exports.queueUpdateDatabaseProfile = exports.queueUpdateDatabaseMember = exports.updateDatabaseProfile = exports.updateDatabaseMember = exports.fetchMemberLeaderboardSpots = exports.fetchLeaderboard = exports.fetchProfileLeaderboard = exports.fetchMemberLeaderboard = exports.fetchAllMemberLeaderboardAttributes = exports.fetchSlayerLeaderboards = exports.fetchAllLeaderboardsCategorized = void 0; const stats_1 = require("./cleaners/skyblock/stats"); const mongodb_1 = require("mongodb"); const cached = __importStar(require("./hypixelCached")); @@ -50,6 +50,7 @@ const reversedLeaderboards = [ let client; let database; let memberLeaderboardsCollection; +let profileLeaderboardsCollection; async function connect() { if (!process.env.db_uri) return console.warn('Warning: db_uri was not found in .env. Features that utilize the database such as leaderboards won\'t work.'); @@ -58,6 +59,7 @@ async function connect() { client = await mongodb_1.MongoClient.connect(process.env.db_uri, { useNewUrlParser: true, useUnifiedTopology: true }); database = client.db(process.env.db_name); memberLeaderboardsCollection = database.collection('member-leaderboards'); + profileLeaderboardsCollection = database.collection('profile-leaderboards'); } function getMemberCollectionAttributes(member) { const collectionAttributes = {}; @@ -106,10 +108,17 @@ function getMemberLeaderboardAttributes(member) { visited_zones: member.visited_zones.length, }; } +function getProfileLeaderboardAttributes(profile) { + // if you want to add a new leaderboard for member attributes, add it here (and getAllLeaderboardAttributes) + return { + minion_count: profile.minion_count + }; +} async function fetchAllLeaderboardsCategorized() { const memberLeaderboardAttributes = await fetchAllMemberLeaderboardAttributes(); + const profileLeaderboardAttributes = await fetchAllProfileLeaderboardAttributes(); const categorizedLeaderboards = {}; - for (const leaderboard of memberLeaderboardAttributes) { + for (const leaderboard of [...memberLeaderboardAttributes, ...profileLeaderboardAttributes]) { const { category } = stats_1.categorizeStat(leaderboard); if (!categorizedLeaderboards[category]) categorizedLeaderboards[category] = []; @@ -140,7 +149,7 @@ async function fetchSlayerLeaderboards() { return leaderboardNames; } exports.fetchSlayerLeaderboards = fetchSlayerLeaderboards; -/** Fetch the names of all the leaderboards */ +/** Fetch the names of all the leaderboards that rank members */ async function fetchAllMemberLeaderboardAttributes() { return [ // we use the raw stat names rather than the clean stats in case hypixel adds a new stat and it takes a while for us to clean it @@ -159,6 +168,12 @@ async function fetchAllMemberLeaderboardAttributes() { ]; } exports.fetchAllMemberLeaderboardAttributes = fetchAllMemberLeaderboardAttributes; +/** Fetch the names of all the leaderboards that rank profiles */ +async function fetchAllProfileLeaderboardAttributes() { + return [ + 'minion_count' + ]; +} function isLeaderboardReversed(name) { for (const leaderboardMatch of reversedLeaderboards) { let trailingEnd = leaderboardMatch[0] === '_'; @@ -171,6 +186,8 @@ function isLeaderboardReversed(name) { return false; } async function fetchMemberLeaderboardRaw(name) { + if (!client) + throw Error('Client isn\'t initialized yet'); if (cachedRawLeaderboards.has(name)) return cachedRawLeaderboards.get(name); // typescript forces us to make a new variable and set it this way because it gives an error otherwise @@ -186,6 +203,23 @@ async function fetchMemberLeaderboardRaw(name) { cachedRawLeaderboards.set(name, leaderboardRaw); return leaderboardRaw; } +async function fetchProfileLeaderboardRaw(name) { + if (cachedRawLeaderboards.has(name)) + return cachedRawLeaderboards.get(name); + // typescript forces us to make a new variable and set it this way because it gives an error otherwise + const query = {}; + query[`stats.${name}`] = { '$exists': true, '$ne': NaN }; + const sortQuery = {}; + sortQuery[`stats.${name}`] = isLeaderboardReversed(name) ? 1 : -1; + const leaderboardRaw = await profileLeaderboardsCollection + .find(query) + .sort(sortQuery) + .limit(leaderboardMax) + .toArray(); + console.log('leaderboardRaw', leaderboardRaw); + cachedRawLeaderboards.set(name, leaderboardRaw); + return leaderboardRaw; +} /** Fetch a leaderboard that ranks members, as opposed to profiles */ async function fetchMemberLeaderboard(name) { var _a; @@ -193,6 +227,7 @@ async function fetchMemberLeaderboard(name) { const fetchLeaderboardPlayer = async (item) => { return { player: await cached.fetchBasicPlayer(item.uuid), + profileUuid: item.profile, value: item.stats[name] }; }; @@ -208,6 +243,44 @@ async function fetchMemberLeaderboard(name) { }; } exports.fetchMemberLeaderboard = fetchMemberLeaderboard; +/** Fetch a leaderboard that ranks profiles, as opposed to members */ +async function fetchProfileLeaderboard(name) { + var _a; + const leaderboardRaw = await fetchProfileLeaderboardRaw(name); + const fetchLeaderboardProfile = async (item) => { + const players = []; + for (const playerUuid of item.players) + players.push(await cached.fetchBasicPlayer(playerUuid)); + return { + players: players, + profileUuid: item.uuid, + value: item.stats[name] + }; + }; + const promises = []; + for (const item of leaderboardRaw) { + promises.push(fetchLeaderboardProfile(item)); + } + const leaderboard = await Promise.all(promises); + return { + name: name, + unit: (_a = stats_1.getStatUnit(name)) !== null && _a !== void 0 ? _a : null, + list: leaderboard + }; +} +exports.fetchProfileLeaderboard = fetchProfileLeaderboard; +/** Fetch a leaderboard */ +async function fetchLeaderboard(name) { + const profileLeaderboards = await fetchAllProfileLeaderboardAttributes(); + console.log(name, profileLeaderboards, profileLeaderboards.includes(name)); + if (profileLeaderboards.includes(name)) { + return await fetchProfileLeaderboard(name); + } + else { + return await fetchMemberLeaderboard(name); + } +} +exports.fetchLeaderboard = fetchLeaderboard; /** Get the leaderboard positions a member is on. This may take a while depending on whether stuff is cached */ async function fetchMemberLeaderboardSpots(player, profile) { var _a; @@ -215,7 +288,7 @@ async function fetchMemberLeaderboardSpots(player, profile) { const fullMember = fullProfile.members.find(m => m.username.toLowerCase() === player.toLowerCase() || m.uuid === player); // update the leaderboard positions for the member await updateDatabaseMember(fullMember, fullProfile); - const applicableAttributes = await getApplicableAttributes(fullMember); + const applicableAttributes = await getApplicableMemberLeaderboardAttributes(fullMember); const memberLeaderboardSpots = []; for (const leaderboardName in applicableAttributes) { const leaderboard = await fetchMemberLeaderboardRaw(leaderboardName); @@ -230,8 +303,12 @@ async function fetchMemberLeaderboardSpots(player, profile) { return memberLeaderboardSpots; } exports.fetchMemberLeaderboardSpots = fetchMemberLeaderboardSpots; -async function getMemberLeaderboardRequirement(name) { - const leaderboard = await fetchMemberLeaderboardRaw(name); +async function getLeaderboardRequirement(name, leaderboardType) { + let leaderboard; + if (leaderboardType === 'member') + leaderboard = await fetchMemberLeaderboardRaw(name); + else if (leaderboardType === 'profile') + leaderboard = await fetchProfileLeaderboardRaw(name); // if there's more than 100 items, return the 100th. if there's less, return null if (leaderboard.length >= leaderboardMax) return leaderboard[leaderboardMax - 1].stats[name]; @@ -239,11 +316,31 @@ async function getMemberLeaderboardRequirement(name) { return null; } /** Get the attributes for the member, but only ones that would put them on the top 100 for leaderboards */ -async function getApplicableAttributes(member) { +async function getApplicableMemberLeaderboardAttributes(member) { const leaderboardAttributes = getMemberLeaderboardAttributes(member); const applicableAttributes = {}; for (const [leaderboard, attributeValue] of Object.entries(leaderboardAttributes)) { - const requirement = await getMemberLeaderboardRequirement(leaderboard); + const requirement = await getLeaderboardRequirement(leaderboard, 'member'); + const leaderboardReversed = isLeaderboardReversed(leaderboard); + if ((requirement === null) + || (leaderboardReversed ? attributeValue < requirement : attributeValue > requirement)) { + applicableAttributes[leaderboard] = attributeValue; + } + } + let leaderboardsCount = Object.keys(applicableAttributes).length; + const leaderboardsCountRequirement = await getLeaderboardRequirement('leaderboards_count', 'member'); + if ((leaderboardsCountRequirement === null) + || (leaderboardsCount > leaderboardsCountRequirement)) { + applicableAttributes['leaderboards_count'] = leaderboardsCount; + } + return applicableAttributes; +} +/** Get the attributes for the profile, but only ones that would put them on the top 100 for leaderboards */ +async function getApplicableProfileLeaderboardAttributes(profile) { + const leaderboardAttributes = getProfileLeaderboardAttributes(profile); + const applicableAttributes = {}; + for (const [leaderboard, attributeValue] of Object.entries(leaderboardAttributes)) { + const requirement = await getLeaderboardRequirement(leaderboard, 'profile'); const leaderboardReversed = isLeaderboardReversed(leaderboard); if ((requirement === null) || (leaderboardReversed ? attributeValue < requirement : attributeValue > requirement)) { @@ -251,10 +348,9 @@ async function getApplicableAttributes(member) { } } let leaderboardsCount = Object.keys(applicableAttributes).length; - const leaderboardsCountRequirement = await getMemberLeaderboardRequirement('leaderboards_count'); + const leaderboardsCountRequirement = await getLeaderboardRequirement('leaderboards_count', 'member'); if ((leaderboardsCountRequirement === null) || (leaderboardsCount > leaderboardsCountRequirement)) { - // add 1 extra because this attribute also counts :) applicableAttributes['leaderboards_count'] = leaderboardsCount; } return applicableAttributes; @@ -279,9 +375,9 @@ async function updateDatabaseMember(member, profile) { await constants.addSlayers(member.slayers.bosses.map(s => s.raw_name)); if (_1.debug) console.log('done constants..'); - const leaderboardAttributes = await getApplicableAttributes(member); + const leaderboardAttributes = await getApplicableMemberLeaderboardAttributes(member); if (_1.debug) - console.log('done getApplicableAttributes..', leaderboardAttributes, member.username, profile.name); + console.log('done getApplicableMemberLeaderboardAttributes..', leaderboardAttributes, member.username, profile.name); await memberLeaderboardsCollection.updateOne({ uuid: member.uuid, profile: profile.uuid @@ -311,15 +407,73 @@ async function updateDatabaseMember(member, profile) { console.log('added member to leaderboards', member.username, leaderboardAttributes); } exports.updateDatabaseMember = updateDatabaseMember; -const leaderboardUpdateQueue = new queue_promise_1.default({ +/** + * Update the profiles's leaderboard data on the server if applicable. + * This will not also update the members, you have to call updateDatabaseMember separately for that + */ +async function updateDatabaseProfile(profile) { + if (_1.debug) + console.log('updateDatabaseProfile', profile.name); + if (!client) + return; // the db client hasn't been initialized + // the profile's been updated too recently, just return + if (recentlyUpdated.get(profile.uuid + 'profile')) + return; + // store the profile in recentlyUpdated so it cant update for 3 more minutes + recentlyUpdated.set(profile.uuid + 'profile', true); + if (_1.debug) + console.log('adding profile to leaderboards', profile.name); + const leaderboardAttributes = await getApplicableProfileLeaderboardAttributes(profile); + if (_1.debug) + console.log('done getApplicableProfileLeaderboardAttributes..', leaderboardAttributes, profile.name); + await profileLeaderboardsCollection.updateOne({ + uuid: profile.uuid + }, { + '$set': { + players: profile.members.map(p => p.uuid), + stats: leaderboardAttributes, + last_updated: new Date() + } + }, { upsert: true }); + // add the profile to the cached leaderboard without having to refetch it + for (const [attributeName, attributeValue] of Object.entries(leaderboardAttributes)) { + const existingRawLeaderboard = await fetchProfileLeaderboardRaw(attributeName); + const leaderboardReverse = isLeaderboardReversed(attributeName); + const newRawLeaderboard = existingRawLeaderboard + // remove the player from the leaderboard, if they're there + .filter(value => value.uuid !== profile.uuid) + .concat([{ + last_updated: new Date(), + stats: leaderboardAttributes, + uuid: profile.uuid, + players: profile.members.map(p => p.uuid) + }]) + .sort((a, b) => leaderboardReverse ? a.stats[attributeName] - b.stats[attributeName] : b.stats[attributeName] - a.stats[attributeName]) + .slice(0, 100); + cachedRawLeaderboards.set(attributeName, newRawLeaderboard); + } + if (_1.debug) + console.log('added profile to leaderboards', profile.name, leaderboardAttributes); +} +exports.updateDatabaseProfile = updateDatabaseProfile; +const leaderboardUpdateMemberQueue = new queue_promise_1.default({ concurrent: 1, interval: 500 }); +const leaderboardUpdateProfileQueue = new queue_promise_1.default({ + concurrent: 1, + interval: 2000 +}); /** Queue an update for the member's leaderboard data on the server if applicable */ async function queueUpdateDatabaseMember(member, profile) { - leaderboardUpdateQueue.enqueue(async () => await updateDatabaseMember(member, profile)); + leaderboardUpdateMemberQueue.enqueue(async () => await updateDatabaseMember(member, profile)); } exports.queueUpdateDatabaseMember = queueUpdateDatabaseMember; +/** Queue an update for the profile's leaderboard data on the server if applicable */ +async function queueUpdateDatabaseProfile(profile) { + leaderboardUpdateProfileQueue.enqueue(async () => await updateDatabaseProfile(profile)); +} +exports.queueUpdateDatabaseProfile = queueUpdateDatabaseProfile; /** * Remove leaderboard attributes for members that wouldn't actually be on the leaderboard. This saves a lot of storage space */ @@ -332,7 +486,7 @@ async function removeBadMemberLeaderboardAttributes() { const unsetValue = {}; unsetValue[leaderboard] = ''; const filter = {}; - const requirement = await getMemberLeaderboardRequirement(leaderboard); + const requirement = await getLeaderboardRequirement(leaderboard, 'member'); const leaderboardReversed = isLeaderboardReversed(leaderboard); if (requirement !== null) { filter[`stats.${leaderboard}`] = { |