osu-api-extended
Version:
Advanced osu! api wrapper for v1 and v2, with extra stuff
184 lines • 7.64 kB
JavaScript
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.namespace = exports.download = exports.request = void 0;
const auth = __importStar(require("../utility/auth"));
const https_1 = __importDefault(require("https"));
const fs_1 = __importDefault(require("fs"));
const generateQueryString = (obj, trail = '') => {
const params = [];
const processValue = (key, value) => {
if (Array.isArray(value)) {
value.forEach((item) => {
params.push(`${key}[]=${encodeURIComponent(item)}`);
});
}
else if (typeof value === 'object' && value !== null) {
const newTrail = trail ? `${trail}.${key}` : key;
params.push(generateQueryString(value, newTrail));
}
else if (typeof value === 'number' && value > 0) {
params.push(`${key}=${value}`);
}
else if (typeof value === 'string') {
params.push(`${key}=${encodeURIComponent(value)}`);
}
};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
if (value !== undefined) {
processValue(key, value);
}
}
}
return params.join('&');
};
const TIMEOUT_MS = 60000;
let amount_retry = 0;
/**
* Executes an HTTP request
* @param {string} url The url
* @returns {Promise<any>} The response
*/
const request = (url, { method = "GET", headers, data, params = {} } = {}) => {
if (url.includes('https://osu.ppy.sh/api/') && !url.includes('https://osu.ppy.sh/api/v2')) {
// @ts-ignore
params.k = params.v1 || auth.cache_v1;
}
if (url.includes('https://osu.ppy.sh/api/v2'))
headers = {
// @ts-ignore
Authorization: `Bearer ${params.v2 || auth.cache_v2}`,
Accept: `application/json`,
'Content-Type': `application/json`,
'x-api-version': '20240130',
};
// console.log({ url, method, headers, data, params: generateQueryString(params) }); // debug too
return new Promise((resolve, reject) => {
const req = https_1.default.request(url + (generateQueryString(params) ? `?${generateQueryString(params)}` : ''), { method, headers }, (response) => {
const chunks = [];
response.on('data', (chunk) => chunks.push(chunk));
response.on('end', async () => {
const data = Buffer.concat(chunks).toString();
if (/^application\/json/.test(response.headers['content-type'])) {
try {
const parse = JSON.parse(data);
if (parse.authentication === 'basic' && auth.cache_v2 && amount_retry < 3) {
await auth.re_login();
amount_retry++;
const again = await (0, exports.request)(url, { method, headers, data, params });
return resolve(again);
}
amount_retry = 0;
return resolve(parse);
}
catch (err) {
console.log(`JSON Parse on content of type ${response.headers['content-type']} failed.\nError: ${err}\nData: ${data}`);
}
}
resolve(data);
});
}).on('error', reject);
req.setTimeout(TIMEOUT_MS, () => {
req.destroy();
reject(new Error(`Request to ${url} time out after ${TIMEOUT_MS}ms`));
});
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, { headers = {}, data, params } = {}, callback) => {
return new Promise((resolve, reject) => {
if (url.includes('https://osu.ppy.sh/api/v2'))
headers['Authorization'] = `Bearer ${auth.cache_v2}`;
headers['accept'] = `application/octet-stream`;
headers['content-Type'] = `application/octet-stream`;
const req = https_1.default.request(url + (params ? '?' + generateQueryString(params) : ''), { method: 'GET', headers }, response => {
const { location } = response.headers;
if (location) {
(0, exports.download)(location, dest, { headers, data, params }, callback)
.then(resolve)
.catch(reject);
return;
}
if (response.statusCode === 404) {
resolve({ error: 'file unavailable' });
return;
}
const file = fs_1.default.createWriteStream(dest, { encoding: 'utf8' });
file.on('error', err => {
fs_1.default.unlinkSync(dest);
reject(err);
});
file.on('finish', () => {
file.close();
resolve(dest);
});
if (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);
});
req.setTimeout(TIMEOUT_MS, () => {
req.destroy();
reject(new Error(`Request to ${url} time out after ${TIMEOUT_MS}ms`));
});
if (data) {
req.write(data);
}
req.end();
});
};
exports.download = download;
/**
* Makes a namespace
* @param {string} url The namespace url
* @param {{ query?: string }} [params] Options ig
* @returns {(params: string, { query: string }) => Promise<any>} The function that does the reqs
*/
const namespace = (url, { query } = {}) => {
return (path, params) => (0, exports.request)(url + path, params);
};
exports.namespace = namespace;
//# sourceMappingURL=request.js.map
;