aboutsummaryrefslogtreecommitdiff
path: root/build/database.js
diff options
context:
space:
mode:
Diffstat (limited to 'build/database.js')
-rw-r--r--build/database.js600
1 files changed, 0 insertions, 600 deletions
diff --git a/build/database.js b/build/database.js
deleted file mode 100644
index cdad69f..0000000
--- a/build/database.js
+++ /dev/null
@@ -1,600 +0,0 @@
-/**
- * Store data about members for leaderboards
-*/
-import { categorizeStat, getStatUnit } from './cleaners/skyblock/stats.js';
-import { slayerLevels } from './cleaners/skyblock/slayers.js';
-import { MongoClient } from 'mongodb';
-import * as cached from './hypixelCached.js';
-import * as constants from './constants.js';
-import { shuffle, sleep } from './util.js';
-import NodeCache from 'node-cache';
-import { v4 as uuid4 } from 'uuid';
-import Queue from 'queue-promise';
-import { debug } from './index.js';
-// don't update the user for 3 minutes
-const recentlyUpdated = new NodeCache({
- stdTTL: 60 * 3,
- checkperiod: 60,
- useClones: false,
-});
-// don't add stuff to the queue within the same 5 minutes
-const recentlyQueued = new NodeCache({
- stdTTL: 60 * 5,
- checkperiod: 60,
- useClones: false,
-});
-export const cachedRawLeaderboards = new Map();
-const leaderboardMax = 100;
-const reversedLeaderboards = [
- 'first_join',
- '_best_time', '_best_time_2'
-];
-let client;
-let database;
-let memberLeaderboardsCollection;
-let profileLeaderboardsCollection;
-let sessionsCollection;
-let accountsCollection;
-const leaderboardInfos = {
- highest_crit_damage: 'This leaderboard is capped at the integer limit because Hypixel, look at the <a href="/leaderboard/highest_critical_damage">highest critical damage leaderboard</a> instead.',
- highest_critical_damage: 'uhhhhh yeah idk either',
- leaderboards_count: 'This leaderboard counts how many leaderboards players are in the top 100 for.',
- top_1_leaderboards_count: 'This leaderboard counts how many leaderboards players are #1 for.',
- skill_social: 'This leaderboard is inaccurate because Hypixel only shows social skill data on some API profiles.'
-};
-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.');
- if (!process.env.db_name)
- return console.warn('Warning: db_name was not found in .env. Features that utilize the database such as leaderboards won\'t work.');
- client = await MongoClient.connect(process.env.db_uri);
- database = client.db(process.env.db_name);
- memberLeaderboardsCollection = database.collection('member-leaderboards');
- profileLeaderboardsCollection = database.collection('profile-leaderboards');
- sessionsCollection = database.collection('sessions');
- accountsCollection = database.collection('accounts');
- console.log('Connected to database :)');
-}
-function getMemberCollectionAttributes(member) {
- const collectionAttributes = {};
- for (const collection of member.collections) {
- const collectionLeaderboardName = `collection_${collection.name}`;
- collectionAttributes[collectionLeaderboardName] = collection.xp;
- }
- return collectionAttributes;
-}
-function getMemberSkillAttributes(member) {
- const skillAttributes = {};
- for (const collection of member.skills) {
- const skillLeaderboardName = `skill_${collection.name}`;
- skillAttributes[skillLeaderboardName] = collection.xp;
- }
- return skillAttributes;
-}
-function getMemberSlayerAttributes(member) {
- const slayerAttributes = {
- slayer_total_xp: member.slayers.xp,
- slayer_total_kills: member.slayers.kills,
- };
- for (const slayer of member.slayers.bosses) {
- slayerAttributes[`slayer_${slayer.raw_name}_total_xp`] = slayer.xp;
- slayerAttributes[`slayer_${slayer.raw_name}_total_kills`] = slayer.kills;
- for (const tier of slayer.tiers) {
- slayerAttributes[`slayer_${slayer.raw_name}_${tier.tier}_kills`] = tier.kills;
- }
- }
- return slayerAttributes;
-}
-function getMemberLeaderboardAttributes(member) {
- // if you want to add a new leaderboard for member attributes, add it here (and getAllLeaderboardAttributes)
- 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
- ...member.rawHypixelStats,
- // collection leaderboards
- ...getMemberCollectionAttributes(member),
- // skill leaderboards
- ...getMemberSkillAttributes(member),
- // slayer leaderboards
- ...getMemberSlayerAttributes(member),
- fairy_souls: member.fairy_souls.total,
- first_join: member.first_join,
- purse: member.purse,
- 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 {
- unique_minions: profile.minion_count
- };
-}
-export async function fetchAllLeaderboardsCategorized() {
- const memberLeaderboardAttributes = await fetchAllMemberLeaderboardAttributes();
- const profileLeaderboardAttributes = await fetchAllProfileLeaderboardAttributes();
- const categorizedLeaderboards = {};
- for (const leaderboard of [...memberLeaderboardAttributes, ...profileLeaderboardAttributes]) {
- const { category } = categorizeStat(leaderboard);
- if (category) {
- if (!categorizedLeaderboards[category])
- categorizedLeaderboards[category] = [];
- categorizedLeaderboards[category].push(leaderboard);
- }
- }
- // move misc to end by removing and readding it
- const misc = categorizedLeaderboards.misc;
- delete categorizedLeaderboards.misc;
- categorizedLeaderboards.misc = misc;
- return categorizedLeaderboards;
-}
-/** Fetch the raw names for the slayer leaderboards */
-export async function fetchSlayerLeaderboards() {
- const rawSlayerNames = await constants.fetchSlayers();
- let leaderboardNames = [
- 'slayer_total_xp',
- 'slayer_total_kills'
- ];
- // we use the raw names (zombie, spider, wolf) instead of the clean names (revenant, tarantula, sven) because the raw names are guaranteed to never change
- for (const slayerNameRaw of rawSlayerNames) {
- leaderboardNames.push(`slayer_${slayerNameRaw}_total_xp`);
- leaderboardNames.push(`slayer_${slayerNameRaw}_total_kills`);
- for (let slayerTier = 1; slayerTier <= slayerLevels; slayerTier++) {
- leaderboardNames.push(`slayer_${slayerNameRaw}_${slayerTier}_kills`);
- }
- }
- return leaderboardNames;
-}
-/** Fetch the names of all the leaderboards that rank members */
-export 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
- ...await constants.fetchStats(),
- // collection leaderboards
- ...(await constants.fetchCollections()).map(value => `collection_${value}`),
- // skill leaderboards
- ...(await constants.fetchSkills()).map(value => `skill_${value}`),
- // slayer leaderboards
- ...await fetchSlayerLeaderboards(),
- 'fairy_souls',
- 'first_join',
- 'purse',
- 'visited_zones',
- 'leaderboards_count',
- 'top_1_leaderboards_count'
- ];
-}
-/** Fetch the names of all the leaderboards that rank profiles */
-async function fetchAllProfileLeaderboardAttributes() {
- return [
- 'unique_minions'
- ];
-}
-function isLeaderboardReversed(name) {
- for (const leaderboardMatch of reversedLeaderboards) {
- let trailingEnd = leaderboardMatch[0] === '_';
- let trailingStart = leaderboardMatch.substr(-1) === '_';
- if ((trailingStart && name.startsWith(leaderboardMatch))
- || (trailingEnd && name.endsWith(leaderboardMatch))
- || (name == leaderboardMatch))
- return true;
- }
- return false;
-}
-/** A set of names of the raw leaderboards that are currently being fetched. This is used to make sure two leaderboads aren't fetched at the same time */
-const fetchingRawLeaderboardNames = new Set();
-async function fetchMemberLeaderboardRaw(name) {
- if (!client)
- throw Error('Client isn\'t initialized yet');
- if (cachedRawLeaderboards.has(name))
- return cachedRawLeaderboards.get(name);
- // if it's currently being fetched, check every 100ms until it's in cachedRawLeaderboards
- if (fetchingRawLeaderboardNames.has(name)) {
- while (true) {
- await sleep(100);
- 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;
- fetchingRawLeaderboardNames.add(name);
- const leaderboardRaw = (await memberLeaderboardsCollection
- .find(query)
- .sort(sortQuery)
- .limit(leaderboardMax)
- .toArray())
- .map((i) => {
- return {
- profile: i.profile,
- uuid: i.uuid,
- value: i.stats[name]
- };
- });
- fetchingRawLeaderboardNames.delete(name);
- cachedRawLeaderboards.set(name, leaderboardRaw);
- return leaderboardRaw;
-}
-async function fetchProfileLeaderboardRaw(name) {
- if (cachedRawLeaderboards.has(name))
- return cachedRawLeaderboards.get(name);
- // if it's currently being fetched, check every 100ms until it's in cachedRawLeaderboards
- if (fetchingRawLeaderboardNames.has(name)) {
- while (true) {
- await sleep(100);
- 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;
- fetchingRawLeaderboardNames.add(name);
- const leaderboardRaw = (await profileLeaderboardsCollection
- .find(query)
- .sort(sortQuery)
- .limit(leaderboardMax)
- .toArray())
- .map((i) => {
- return {
- players: i.players,
- uuid: i.uuid,
- value: i.stats[name]
- };
- });
- fetchingRawLeaderboardNames.delete(name);
- cachedRawLeaderboards.set(name, leaderboardRaw);
- return leaderboardRaw;
-}
-/** Fetch a leaderboard that ranks members, as opposed to profiles */
-export async function fetchMemberLeaderboard(name) {
- const leaderboardRaw = await fetchMemberLeaderboardRaw(name);
- const fetchLeaderboardPlayer = async (i) => {
- const player = await cached.fetchBasicPlayer(i.uuid);
- return {
- player,
- profileUuid: i.profile,
- value: i.value
- };
- };
- const promises = [];
- for (const item of leaderboardRaw) {
- promises.push(fetchLeaderboardPlayer(item));
- }
- const leaderboard = await Promise.all(promises);
- return {
- name: name,
- unit: getStatUnit(name) ?? null,
- list: leaderboard
- };
-}
-/** Fetch a leaderboard that ranks profiles, as opposed to members */
-export async function fetchProfileLeaderboard(name) {
- const leaderboardRaw = await fetchProfileLeaderboardRaw(name);
- const fetchLeaderboardProfile = async (i) => {
- const players = [];
- for (const playerUuid of i.players) {
- const player = await cached.fetchBasicPlayer(playerUuid);
- if (player)
- players.push(player);
- }
- return {
- players: players,
- profileUuid: i.uuid,
- value: i.value
- };
- };
- const promises = [];
- for (const item of leaderboardRaw) {
- promises.push(fetchLeaderboardProfile(item));
- }
- const leaderboard = await Promise.all(promises);
- return {
- name: name,
- unit: getStatUnit(name) ?? null,
- list: leaderboard
- };
-}
-/** Fetch a leaderboard */
-export async function fetchLeaderboard(name) {
- const profileLeaderboards = await fetchAllProfileLeaderboardAttributes();
- let leaderboard;
- if (profileLeaderboards.includes(name)) {
- leaderboard = await fetchProfileLeaderboard(name);
- }
- else {
- leaderboard = await fetchMemberLeaderboard(name);
- }
- if (leaderboardInfos[name])
- leaderboard.info = leaderboardInfos[name];
- return leaderboard;
-}
-/** Get the leaderboard positions a member is on. This may take a while depending on whether stuff is cached */
-export async function fetchMemberLeaderboardSpots(player, profile) {
- const fullProfile = await cached.fetchProfile(player, profile);
- if (!fullProfile)
- return null;
- const fullMember = fullProfile.members.find(m => m.username.toLowerCase() === player.toLowerCase() || m.uuid === player);
- if (!fullMember)
- return null;
- // update the leaderboard positions for the member
- await updateDatabaseMember(fullMember, fullProfile);
- const applicableAttributes = await getApplicableMemberLeaderboardAttributes(fullMember);
- const memberLeaderboardSpots = [];
- for (const leaderboardName in applicableAttributes) {
- const leaderboard = await fetchMemberLeaderboardRaw(leaderboardName);
- const leaderboardPositionIndex = leaderboard.findIndex(i => i.uuid === fullMember.uuid && i.profile === fullProfile.uuid);
- memberLeaderboardSpots.push({
- name: leaderboardName,
- positionIndex: leaderboardPositionIndex,
- value: applicableAttributes[leaderboardName],
- unit: getStatUnit(leaderboardName) ?? null
- });
- }
- return memberLeaderboardSpots;
-}
-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
- return {
- top_100: leaderboard[leaderboardMax - 1]?.value ?? null,
- top_1: leaderboard[1]?.value ?? null
- };
-}
-/** Get the attributes for the member, but only ones that would put them on the top 100 for leaderboards */
-async function getApplicableMemberLeaderboardAttributes(member) {
- const leaderboardAttributes = getMemberLeaderboardAttributes(member);
- const applicableAttributes = {};
- const applicableTop1Attributes = {};
- for (const [leaderboard, attributeValue] of Object.entries(leaderboardAttributes)) {
- const requirement = await getLeaderboardRequirement(leaderboard, 'member');
- const leaderboardReversed = isLeaderboardReversed(leaderboard);
- if ((requirement.top_100 === null)
- || (leaderboardReversed ? attributeValue < requirement.top_100 : attributeValue > requirement.top_100)) {
- applicableAttributes[leaderboard] = attributeValue;
- }
- if ((requirement.top_1 === null)
- || (leaderboardReversed ? attributeValue < requirement.top_1 : attributeValue > requirement.top_1)) {
- applicableTop1Attributes[leaderboard] = attributeValue;
- }
- }
- // add the "leaderboards count" attribute
- const leaderboardsCount = Object.keys(applicableAttributes).length;
- const leaderboardsCountRequirement = await getLeaderboardRequirement('leaderboards_count', 'member');
- if (leaderboardsCount > 0
- && ((leaderboardsCountRequirement.top_100 === null)
- || (leaderboardsCount > leaderboardsCountRequirement.top_100)))
- applicableAttributes['leaderboards_count'] = leaderboardsCount;
- // add the "first leaderboards count" attribute
- const top1LeaderboardsCount = Object.keys(applicableTop1Attributes).length;
- const top1LeaderboardsCountRequirement = await getLeaderboardRequirement('top_1_leaderboards_count', 'member');
- if (top1LeaderboardsCount > 0
- && ((top1LeaderboardsCountRequirement.top_100 === null)
- || (top1LeaderboardsCount > top1LeaderboardsCountRequirement.top_100)))
- applicableAttributes['top_1_leaderboards_count'] = top1LeaderboardsCount;
- 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 = {};
- const applicableTop1Attributes = {};
- for (const [leaderboard, attributeValue] of Object.entries(leaderboardAttributes)) {
- const requirement = await getLeaderboardRequirement(leaderboard, 'profile');
- const leaderboardReversed = isLeaderboardReversed(leaderboard);
- if ((requirement.top_100 === null)
- || (leaderboardReversed ? attributeValue < requirement.top_100 : attributeValue > requirement.top_100
- && attributeValue !== 0)) {
- applicableAttributes[leaderboard] = attributeValue;
- }
- if ((requirement.top_1 === null)
- || (leaderboardReversed ? attributeValue < requirement.top_1 : attributeValue > requirement.top_1
- && attributeValue !== 0)) {
- applicableTop1Attributes[leaderboard] = attributeValue;
- }
- }
- return applicableAttributes;
-}
-/** Update the member's leaderboard data on the server if applicable */
-export async function updateDatabaseMember(member, profile) {
- if (!client)
- return; // the db client hasn't been initialized
- if (debug)
- console.debug('updateDatabaseMember', member.username);
- // the member's been updated too recently, just return
- if (recentlyUpdated.get(profile.uuid + member.uuid))
- return;
- // store the member in recentlyUpdated so it cant update for 3 more minutes
- recentlyUpdated.set(profile.uuid + member.uuid, true);
- if (debug)
- console.debug('adding member to leaderboards', member.username);
- if (member.rawHypixelStats)
- await constants.addStats(Object.keys(member.rawHypixelStats));
- await constants.addCollections(member.collections.map(coll => coll.name));
- await constants.addSkills(member.skills.map(skill => skill.name));
- await constants.addZones(member.visited_zones.map(zone => zone.name));
- await constants.addSlayers(member.slayers.bosses.map(s => s.raw_name));
- if (debug)
- console.debug('done constants..');
- const leaderboardAttributes = await getApplicableMemberLeaderboardAttributes(member);
- if (debug)
- console.debug('done getApplicableMemberLeaderboardAttributes..', leaderboardAttributes, member.username, profile.name);
- await memberLeaderboardsCollection.updateOne({
- uuid: member.uuid,
- profile: profile.uuid
- }, {
- '$set': {
- stats: leaderboardAttributes,
- last_updated: new Date()
- }
- }, { upsert: true });
- for (const [attributeName, attributeValue] of Object.entries(leaderboardAttributes)) {
- const existingRawLeaderboard = await fetchMemberLeaderboardRaw(attributeName);
- const leaderboardReverse = isLeaderboardReversed(attributeName);
- const newRawLeaderboard = existingRawLeaderboard
- // remove the player from the leaderboard, if they're there
- .filter(value => value.uuid !== member.uuid || value.profile !== profile.uuid)
- .concat([{
- value: attributeValue,
- uuid: member.uuid,
- profile: profile.uuid
- }])
- .sort((a, b) => leaderboardReverse ? a.value - b.value : b.value - a.value)
- .slice(0, 100);
- cachedRawLeaderboards.set(attributeName, newRawLeaderboard);
- }
- if (debug)
- console.debug('added member to leaderboards', member.username, leaderboardAttributes);
-}
-/**
- * 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
- */
-export async function updateDatabaseProfile(profile) {
- if (!client)
- return; // the db client hasn't been initialized
- if (debug)
- console.debug('updateDatabaseProfile', profile.name);
- // 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 (debug)
- console.debug('adding profile to leaderboards', profile.name);
- const leaderboardAttributes = await getApplicableProfileLeaderboardAttributes(profile);
- if (debug)
- console.debug('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([{
- value: attributeValue,
- uuid: profile.uuid,
- players: profile.members.map(p => p.uuid)
- }])
- .sort((a, b) => leaderboardReverse ? a.value - b.value : b.value - a.value)
- .slice(0, 100);
- cachedRawLeaderboards.set(attributeName, newRawLeaderboard);
- }
- if (debug)
- console.debug('added profile to leaderboards', profile.name, leaderboardAttributes);
-}
-export const leaderboardUpdateMemberQueue = new Queue({
- concurrent: 1,
- interval: 50
-});
-export const leaderboardUpdateProfileQueue = new Queue({
- concurrent: 1,
- interval: 500
-});
-/** Queue an update for the member's leaderboard data on the server if applicable */
-export function queueUpdateDatabaseMember(member, profile) {
- if (recentlyQueued.get(profile.uuid + member.uuid))
- return;
- else
- recentlyQueued.set(profile.uuid + member.uuid, true);
- leaderboardUpdateMemberQueue.enqueue(async () => await updateDatabaseMember(member, profile));
-}
-/** Queue an update for the profile's leaderboard data on the server if applicable */
-export function queueUpdateDatabaseProfile(profile) {
- if (recentlyQueued.get(profile.uuid + 'profile'))
- return;
- else
- recentlyQueued.set(profile.uuid + 'profile', true);
- leaderboardUpdateProfileQueue.enqueue(async () => await updateDatabaseProfile(profile));
-}
-/**
- * Remove leaderboard attributes for members that wouldn't actually be on the leaderboard. This saves a lot of storage space
- */
-async function removeBadMemberLeaderboardAttributes() {
- const leaderboards = await fetchAllMemberLeaderboardAttributes();
- // shuffle so if the application is restarting many times itll still be useful
- for (const leaderboard of shuffle(leaderboards)) {
- // wait 10 seconds so it doesnt use as much ram
- await sleep(10 * 1000);
- const unsetValue = {};
- unsetValue[leaderboard] = '';
- const filter = {};
- const requirement = await getLeaderboardRequirement(leaderboard, 'member');
- const leaderboardReversed = isLeaderboardReversed(leaderboard);
- if (requirement !== null) {
- filter[`stats.${leaderboard}`] = {
- '$lt': leaderboardReversed ? undefined : requirement,
- '$gt': leaderboardReversed ? requirement : undefined
- };
- await memberLeaderboardsCollection.updateMany(filter, { '$unset': unsetValue });
- }
- }
- await memberLeaderboardsCollection.deleteMany({ stats: {} });
- await profileLeaderboardsCollection.deleteMany({ stats: {} });
-}
-export let finishedCachingRawLeaderboards = false;
-/** Fetch all the leaderboards, used for caching. Don't call this often! */
-async function fetchAllLeaderboards(fast) {
- const leaderboards = await fetchAllMemberLeaderboardAttributes();
- if (debug)
- console.debug('Caching raw leaderboards!');
- for (const leaderboard of shuffle(leaderboards))
- await fetchMemberLeaderboardRaw(leaderboard);
- finishedCachingRawLeaderboards = true;
-}
-export async function createSession(refreshToken, userData) {
- const sessionId = uuid4();
- await sessionsCollection?.insertOne({
- _id: sessionId,
- refresh_token: refreshToken,
- discord_user: {
- id: userData.id,
- name: userData.username + '#' + userData.discriminator
- },
- lastUpdated: new Date()
- });
- return sessionId;
-}
-export async function fetchSession(sessionId) {
- return await sessionsCollection?.findOne({ _id: sessionId });
-}
-export async function fetchAccount(minecraftUuid) {
- return await accountsCollection?.findOne({ minecraftUuid });
-}
-export async function fetchAccountFromDiscord(discordId) {
- return await accountsCollection?.findOne({ discordId });
-}
-export async function updateAccount(discordId, schema) {
- await accountsCollection?.updateOne({
- discordId
- }, { $set: schema }, { upsert: true });
-}
-// make sure it's not in a test
-console.log('global.isTest', globalThis.isTest);
-if (!globalThis.isTest) {
- connect().then(() => {
- // when it connects, cache the leaderboards and remove bad members
- removeBadMemberLeaderboardAttributes();
- // cache leaderboards on startup so its faster later on
- fetchAllLeaderboards(true);
- // cache leaderboard players again every 4 hours
- setInterval(fetchAllLeaderboards, 4 * 60 * 60 * 1000);
- });
-}