"use strict"; /** * Fetch and edit constants from the skyblock-constants repo */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.setConstantValues = exports.fetchConstantValues = exports.addMinions = exports.fetchSkillXpEasier = exports.fetchSkillXp = exports.fetchMinions = exports.addSlayers = exports.fetchSlayers = exports.addZones = exports.fetchZones = exports.addSkills = exports.fetchSkills = exports.addCollections = exports.fetchCollections = exports.addStats = exports.fetchStats = exports.addJSONConstants = exports.fetchJSONConstant = void 0; // we have to do this so we can mock the function from the tests properly const constants = __importStar(require("./constants")); const node_cache_1 = __importDefault(require("node-cache")); const queue_promise_1 = __importDefault(require("queue-promise")); const node_fetch_1 = __importDefault(require("node-fetch")); const https_1 = require("https"); const _1 = require("."); const httpsAgent = new https_1.Agent({ keepAlive: true }); const githubApiBase = 'https://api.github.com'; const owner = 'skyblockstats'; const repo = 'skyblock-constants'; // we use a queue for editing so it always utilizes the cache if possible, and to avoid hitting the github rateimit const queue = new queue_promise_1.default({ concurrent: 1, interval: 10 }); /** * Send a request to the GitHub API * @param method The HTTP method, for example GET, PUT, POST, etc * @param route The route to send the request to * @param headers The extra headers * @param json The JSON body, only applicable for some types of methods */ async function fetchGithubApi(method, route, headers, json) { try { if (_1.debug) console.debug('fetching github api', method, route); const data = await node_fetch_1.default(githubApiBase + route, { agent: () => httpsAgent, body: json ? JSON.stringify(json) : undefined, method, headers: Object.assign({ 'Authorization': `token ${process.env.github_token}` }, headers), }); if (_1.debug) console.debug('fetched github api', method, route); return data; } catch { // if there's an error, wait a second and try again await new Promise((resolve) => setTimeout(resolve, 1000)); return await fetchGithubApi(method, route, headers, json); } } // cache files for an hour const fileCache = new node_cache_1.default({ stdTTL: 60 * 60, checkperiod: 60, useClones: false, }); /** * Fetch a file from skyblock-constants * @param path The file path, for example stats.json */ function fetchFile(path) { return new Promise(resolve => { queue.enqueue(async () => { if (fileCache.has(path)) return resolve(fileCache.get(path)); const r = await fetchGithubApi('GET', `/repos/${owner}/${repo}/contents/${path}`, { 'Accept': 'application/vnd.github.v3+json', }); const data = await r.json(); const file = { path: data.path, content: Buffer.from(data.content, data.encoding).toString(), sha: data.sha }; fileCache.set(path, file); resolve(file); }); }); } /** * Edit a file on skyblock-constants * @param file The GithubFile you got from fetchFile * @param message The commit message * @param newContent The new content in the file */ async function editFile(file, message, newContent) { const r = await fetchGithubApi('PUT', `/repos/${owner}/${repo}/contents/${file.path}`, { 'Content-Type': 'application/json' }, { message: message, content: Buffer.from(newContent).toString('base64'), sha: file.sha, branch: 'main' }); const data = await r.json(); fileCache.set(file.path, { path: data.content.path, content: newContent, sha: data.content.sha }); } async function fetchJSONConstant(filename) { const file = await fetchFile(filename); try { return JSON.parse(file.content); } catch { // probably invalid json, return an empty array return []; } } exports.fetchJSONConstant = fetchJSONConstant; /** Add stats to skyblock-constants. This has caching so it's fine to call many times */ async function addJSONConstants(filename, addingValues, unit = 'stat') { if (addingValues.length === 0) return; // no stats provided, just return let file = await fetchFile(filename); if (!file.path) return; let oldStats; try { oldStats = JSON.parse(file.content); } catch { // invalid json, set it as an empty array oldStats = []; } const updatedStats = oldStats .concat(addingValues) // remove duplicates .filter((value, index, array) => array.indexOf(value) === index) .sort((a, b) => a.localeCompare(b)); const newStats = updatedStats.filter(value => !oldStats.includes(value)); // there's not actually any new stats, just return if (newStats.length === 0) return; const commitMessage = newStats.length >= 2 ? `Add ${newStats.length} new ${unit}s` : `Add '${newStats[0]}' ${unit}`; try { await editFile(file, commitMessage, JSON.stringify(updatedStats, null, 2)); } catch { // the file probably changed or something, try again file = await fetchFile(filename); await editFile(file, commitMessage, JSON.stringify(updatedStats, null, 2)); } } exports.addJSONConstants = addJSONConstants; /** Fetch all the known SkyBlock stats as an array of strings */ async function fetchStats() { return await constants.fetchJSONConstant('stats.json'); } exports.fetchStats = fetchStats; /** Add stats to skyblock-constants. This has caching so it's fine to call many times */ async function addStats(addingStats) { await constants.addJSONConstants('stats.json', addingStats, 'stat'); } exports.addStats = addStats; /** Fetch all the known SkyBlock collections as an array of strings */ async function fetchCollections() { return await constants.fetchJSONConstant('collections.json'); } exports.fetchCollections = fetchCollections; /** Add collections to skyblock-constants. This has caching so it's fine to call many times */ async function addCollections(addingCollections) { await constants.addJSONConstants('collections.json', addingCollections, 'collection'); } exports.addCollections = addCollections; /** Fetch all the known SkyBlock collections as an array of strings */ async function fetchSkills() { return await constants.fetchJSONConstant('skills.json'); } exports.fetchSkills = fetchSkills; /** Add skills to skyblock-constants. This has caching so it's fine to call many times */ async function addSkills(addingSkills) { await constants.addJSONConstants('skills.json', addingSkills, 'skill'); } exports.addSkills = addSkills; /** Fetch all the known SkyBlock collections as an array of strings */ async function fetchZones() { return await constants.fetchJSONConstant('zones.json'); } exports.fetchZones = fetchZones; /** Add skills to skyblock-constants. This has caching so it's fine to call many times */ async function addZones(addingZones) { await constants.addJSONConstants('zones.json', addingZones, 'zone'); } exports.addZones = addZones; /** Fetch all the known SkyBlock slayer names as an array of strings */ async function fetchSlayers() { return await constants.fetchJSONConstant('slayers.json'); } exports.fetchSlayers = fetchSlayers; /** Add skills to skyblock-constants. This has caching so it's fine to call many times */ async function addSlayers(addingSlayers) { await constants.addJSONConstants('slayers.json', addingSlayers, 'slayer'); } exports.addSlayers = addSlayers; /** Fetch all the known SkyBlock slayer names as an array of strings */ async function fetchMinions() { return await constants.fetchJSONConstant('minions.json'); } exports.fetchMinions = fetchMinions; async function fetchSkillXp() { return await constants.fetchJSONConstant('manual/skill_xp.json'); } exports.fetchSkillXp = fetchSkillXp; async function fetchSkillXpEasier() { return await constants.fetchJSONConstant('manual/skill_xp_easier.json'); } exports.fetchSkillXpEasier = fetchSkillXpEasier; /** Add skills to skyblock-constants. This has caching so it's fine to call many times */ async function addMinions(addingMinions) { await constants.addJSONConstants('minions.json', addingMinions, 'minion'); } exports.addMinions = addMinions; async function fetchConstantValues() { return await constants.fetchJSONConstant('values.json'); } exports.fetchConstantValues = fetchConstantValues; async function setConstantValues(newValues) { let file = await fetchFile('values.json'); if (!file.path) return; let oldValues; try { oldValues = JSON.parse(file.content); } catch { // invalid json, set it as an empty array oldValues = {}; } const updatedStats = { ...oldValues, ...newValues }; // there's not actually any new stats, just return // TODO: optimize this? might be fine already though, idk if (JSON.stringify(updatedStats) === JSON.stringify(oldValues)) return; const commitMessage = 'Update values'; try { await editFile(file, commitMessage, JSON.stringify(updatedStats, null, 2)); } catch { } } exports.setConstantValues = setConstantValues;