UNPKG

osu-api-extended

Version:

Advanced osu! api wrapper cover all V2 and V1 endpoints, and provide useful tools

283 lines (282 loc) 13.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.download = exports.request = void 0; // PACKAGES const querystring_1 = __importDefault(require("querystring")); const https_1 = __importDefault(require("https")); const fs_1 = __importDefault(require("fs")); // CREDENTIALS const auth = __importStar(require("./auth")); ; // VALUES let total_retries = 0; const sanitize_query = (obj) => { const object = Object.assign({}, obj); const array = Object.keys(object); for (let i = 0; i < array.length; i++) { const key = array[i]; const value = object[key]; if (value != null) continue; delete object[key]; } ; return querystring_1.default.encode(object); }; const request = (url, { method, headers, data, params = {}, addons = {} }) => new Promise((resolve, reject) => { // check required args if (url == null) { return resolve({ error: 'URL not specified', }); } ; if (method == null) { return resolve({ error: 'Method not specified', }); } ; // V1 add credentials if (url.includes('https://osu.ppy.sh/api/') && !url.includes('https://osu.ppy.sh/api/v2')) { params.k = addons.authKey || auth.cache.v1; if (params.k == null) { return resolve({ error: 'v1 api key not specified' }); } ; } ; // V2 add credentials if (url.includes('https://osu.ppy.sh/api/v2')) { if (!headers) headers = {}; headers.Authorization = `Bearer ${addons.authKey || auth.cache.v2}`; if (!headers.Accept) headers.Accept = `application/json`; if (!headers['Content-Type']) headers['Content-Type'] = `application/json`; headers['x-api-version'] = addons.apiVersion == '' ? null : addons.apiVersion || '20240130'; } ; if (addons.legacy_only != null) params.legacy_only = addons.legacy_only == true ? 1 : 0; const generate_query = sanitize_query(params); const build_url = url + (generate_query ? `?${generate_query}` : ''); // console.log({ url: build_url, method, headers, data, generate_query, params }); // debug const req = https_1.default.request(build_url, { method, headers }, (response) => { const { location } = response.headers; if (response.headers['x-ratelimit-limit']) auth.cache['ratelimit-limit'] = parseInt(response.headers['x-ratelimit-limit'].toString() || '60'); if (response.headers['x-ratelimit-remaining']) auth.cache['ratelimit-remaining'] = parseInt(response.headers['x-ratelimit-remaining'].toString() || '60'); if (location) { (0, exports.request)(location, { method, headers, data, params, addons }) .then(resolve) .catch(reject); return; } ; // console.log(response.statusCode, response.statusMessage, response.headers.accept, { // 'ratelimit-limit': auth.cache['ratelimit-limit'], // 'ratelimit-remaining': auth.cache['ratelimit-remaining'], // }, { // url: build_url, method, headers, data, generate_query, params, // }); // debug const chunks = []; // handle response events response.on('data', (chunk) => chunks.push(chunk)); response.on('end', () => __awaiter(void 0, void 0, void 0, function* () { const chunks_data = Buffer.concat(chunks).toString(); if (/^application\/json/.test(response.headers['content-type'])) { try { const parse = JSON.parse(chunks_data); if (parse.authentication === 'basic' && addons.ignoreSessionRefresh != true) { if (total_retries > 3) { return resolve({ error: 'Unnable to refresh session attempted 3 times, double check your credentials (or report to package author)' }); } ; total_retries++; const refresh = yield auth.refresh_token(); if (refresh == null) { return resolve({ error: 'Cannot refresh session, double check your credentials (or report to package author)' }); } ; const retry_request = yield (0, exports.request)(url, { method, headers, data, params }); return resolve(retry_request); } ; if ('error' in parse && Object.keys(parse).length == 1) { if (parse.error === null) { return resolve({ error: `osu returned empty error, double check your parameters (request)` }); } ; return resolve({ error: parse.error }); } ; total_retries = 0; if (response.statusCode != 200) return resolve({ error: `${response.statusCode}: ${response.statusMessage}` }); return resolve(parse); } catch (error) { return resolve({ error: error.name }); } ; } ; if (response.statusCode != 200) return resolve({ error: `${response.statusCode}: ${response.statusMessage}` }); return resolve(chunks_data); })); }); // send error req.on('error', (error) => { resolve({ error: error.name }); }); // timeout req.setTimeout(addons.timeout_ms || auth.settings.timeout, () => { req.destroy(); resolve({ error: `Request to ${build_url} time out after ${addons.timeout_ms || auth.settings.timeout}ms` }); }); // write body to request, if specified if (data) req.write(data); req.end(); }); exports.request = request; /** * Executes an HTTP request * @param {string} url The url * @param {string} dest The file destination * @returns {Promise<any>} The response */ const download = (url, dest, { _callback, headers = {}, data, params, callback, addons = {} }) => { return new Promise((resolve, reject) => { const start_time = performance.now(); if (url.includes('https://osu.ppy.sh/api/v2')) headers['Authorization'] = `Bearer ${(params === null || params === void 0 ? void 0 : params.v2) || auth.cache.v2}`; if (!headers['accept']) headers['accept'] = `application/octet-stream`; const generate_query = sanitize_query(params); const build_url = url + (generate_query ? `?${generate_query}` : ''); // console.log({ url: build_url, headers, data }); // debug const req = https_1.default.request(build_url, { method: 'GET', headers }, response => { const { location } = response.headers; // console.log(url, response.headers['content-type'], response.headers); // debug too if (location) { (0, exports.download)(location, dest, { _callback, headers, data, params, callback, addons }) .then(resolve) .catch(error => ({ error: error.message })); return; } ; if (response.statusCode === 404) { return resolve({ error: 'file unavailable' }); } ; if (response.headers['content-type'] == 'application/json' || (response.headers['content-type'] == null && +(response.headers['content-length'] || 0) < 150)) { const chunks = []; response.on('data', (chunk) => chunks.push(chunk)); response.on('end', () => __awaiter(void 0, void 0, void 0, function* () { const data = Buffer.concat(chunks).toString(); try { const parse = JSON.parse(data); if (parse.authentication === 'basic' && addons.ignoreSessionRefresh != true) { if (total_retries > 3) { return resolve({ error: 'Unnable to refresh session attempted 3 times, double check your credentials (or report to package author)' }); } ; total_retries++; const refresh = yield auth.refresh_token(); if (refresh == null) { return resolve({ error: 'Cannot refresh session, double check your credentials (or report to package author)' }); } ; const retry_request = yield (0, exports.download)(url, dest, { _callback, headers, data, params, callback, addons }); return resolve(retry_request); } ; if ('error' in parse && parse.error == null) { return resolve({ error: 'osu returned empty error (download)' }); } ; return resolve(parse); } catch (error) { return resolve({ error: `Unable to download file: ${data} (${url}): ${error === null || error === void 0 ? void 0 : error.message}` }); } ; })); return; } ; const file = fs_1.default.createWriteStream(dest, { encoding: 'utf8' }); file.on('error', error => { fs_1.default.unlinkSync(dest); resolve({ error: error.message }); }); file.on('finish', () => { file.close(); const finish_time = performance.now(); resolve({ status: 'finished', destination: dest, elapsed_time: finish_time - start_time }); }); if (_callback == true && callback !== undefined) { const totalLength = parseInt(response.headers['content-length']); let progress = 0; let progressBar = 0; response.on('data', (chunk) => { progress += chunk.length; progressBar = 100 * (progress / totalLength); callback(progressBar); }); } response.pipe(file); }); // send error req.on('error', (error) => { resolve({ error: error.message }); }); // timeout req.setTimeout(addons.timeout_ms || auth.settings.timeout, () => { req.destroy(); resolve({ error: `Request to ${build_url} time out after ${addons.timeout_ms || auth.settings.timeout}ms` }); }); if (data) { req.write(data); } ; req.end(); }); }; exports.download = download;