diff --git a/.prettierignore b/.prettierignore index ce0aba88..cbce3e95 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ .vscode/ node_modules/ dist/ -static/ \ No newline at end of file +static/ +packages/server/assets/homepage.js \ No newline at end of file diff --git a/packages/common/index.ts b/packages/common/index.ts index 52d7762c..969890de 100644 --- a/packages/common/index.ts +++ b/packages/common/index.ts @@ -4,3 +4,5 @@ export * from './utils/platforms'; export * from './utils/agruments'; export * from './utils/sleep'; export * from './utils/config'; +export * from './utils/unzip'; +export * from './utils/directories'; diff --git a/packages/common/package.json b/packages/common/package.json index c43d8666..38101b51 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -10,6 +10,7 @@ "build": "tsc" }, "dependencies": { + "adm-zip": "^0.5.10", "dotenv": "^16.0.3", "winston": "^3.8.2" } diff --git a/packages/common/utils/config.ts b/packages/common/utils/config.ts index b2bb11ce..56a48dff 100644 --- a/packages/common/utils/config.ts +++ b/packages/common/utils/config.ts @@ -5,9 +5,7 @@ import path from 'path'; import { configureLogger, wLogger } from './logger'; const configPath = path.join( - process.env.NODE_ENV === 'development' - ? process.cwd() - : path.dirname(process.execPath), + 'pkg' in process ? path.dirname(process.execPath) : process.cwd(), 'tsosu.env' ); if (!fs.existsSync(configPath)) { @@ -45,7 +43,8 @@ SERVER_IP=127.0.0.1 # The port on which the websocket api server will run SERVER_PORT=24050 # The folder from which the overlays will be taken. -STATIC_FOLDER_PATH=./static` +STATIC_FOLDER_PATH=./static`, + 'utf8' ); } @@ -107,7 +106,7 @@ export const updateConfigFile = () => { } if (!process.env.ENABLE_GOSU_OVERLAY) { - newOptions += 'nENABLE_GOSU_OVERLAY, '; + newOptions += 'ENABLE_GOSU_OVERLAY, '; fs.appendFileSync(configPath, '\nENABLE_GOSU_OVERLAY=false', 'utf8'); } @@ -127,6 +126,7 @@ export const watchConfigFile = ({ httpServer }: { httpServer: any }) => { }; export const refreshConfig = (httpServer: any, refresh: boolean) => { + let updated = false; const status = refresh == true ? 'reload' : 'load'; const { parsed, error } = dotenv.config({ path: configPath }); @@ -136,9 +136,26 @@ export const refreshConfig = (httpServer: any, refresh: boolean) => { } const debugLogging = (parsed.DEBUG_LOG || '') === 'true'; - const serverIP = parsed.SERVER_IP || '127.0.0.1'; const serverPort = Number(parsed.SERVER_PORT || '24050'); + const calculatePP = (parsed.CALCULATE_PP || '') === 'true'; + const enableKeyOverlay = (parsed.ENABLE_KEY_OVERLAY || '') === 'true'; + const pollRate = Number(parsed.POLL_RATE || '500'); + const keyOverlayPollRate = Number(parsed.KEYOVERLAY_POLL_RATE || '100'); + const staticFolderPath = parsed.STATIC_FOLDER_PATH || './static'; + const enableGosuOverlay = (parsed.ENABLE_GOSU_OVERLAY || '') === 'true'; + + // determine whether config actually was updated or not + updated = + config.debugLogging != debugLogging || + config.calculatePP != calculatePP || + config.enableKeyOverlay != enableKeyOverlay || + config.pollRate != pollRate || + config.keyOverlayPollRate != keyOverlayPollRate || + config.staticFolderPath != staticFolderPath || + config.enableGosuOverlay != enableGosuOverlay || + config.serverIP != serverIP || + config.serverPort != serverPort; if (config.serverIP != serverIP || config.serverPort != serverPort) { config.serverIP = serverIP; @@ -148,12 +165,13 @@ export const refreshConfig = (httpServer: any, refresh: boolean) => { } config.debugLogging = debugLogging; - config.calculatePP = (parsed.CALCULATE_PP || '') === 'true'; - config.enableKeyOverlay = (parsed.ENABLE_KEY_OVERLAY || '') === 'true'; - config.pollRate = Number(parsed.POLL_RATE || '500'); - config.keyOverlayPollRate = Number(parsed.KEYOVERLAY_POLL_RATE || '100'); - config.staticFolderPath = parsed.STATIC_FOLDER_PATH || './static'; - config.enableGosuOverlay = (parsed.ENABLE_GOSU_OVERLAY || '') === 'true'; + config.calculatePP = calculatePP; + config.enableKeyOverlay = enableKeyOverlay; + config.pollRate = pollRate >= 0 ? pollRate : 100; + config.keyOverlayPollRate = + keyOverlayPollRate >= 0 ? keyOverlayPollRate : 100; + config.staticFolderPath = staticFolderPath; + config.enableGosuOverlay = enableGosuOverlay; if ( config.staticFolderPath == './static' && @@ -162,6 +180,11 @@ export const refreshConfig = (httpServer: any, refresh: boolean) => { fs.mkdirSync(path.join(process.cwd(), 'static')); } - wLogger.info(`Config ${status}ed`); + if (updated) wLogger.info(`Config ${status}ed`); configureLogger(); }; + +export const writeConfig = (httpServer: any, text: string) => { + fs.writeFileSync(configPath, text, 'utf8'); + refreshConfig(httpServer, true); +}; diff --git a/packages/common/utils/directories.ts b/packages/common/utils/directories.ts new file mode 100644 index 00000000..4f2e57e0 --- /dev/null +++ b/packages/common/utils/directories.ts @@ -0,0 +1,29 @@ +import fs from 'fs'; +import path from 'path'; + +export function recursiveFilesSearch({ + dir, + filename, + fileList +}: { + dir: string; + filename: string; + fileList: { filePath: string; created: number }[]; +}) { + const files = fs.readdirSync(dir); + files.forEach((file) => { + const filePath = path.join(dir, file); + const stats = fs.statSync(filePath); + if (stats.isDirectory()) { + recursiveFilesSearch({ dir: filePath, filename, fileList }); + } else if (filePath.includes(filename)) { + const stats = fs.statSync(filePath); + + fileList.push({ filePath, created: stats.mtimeMs }); + } + }); + + fileList.sort((a, b) => a.created - b.created); + + return fileList.map((r) => r.filePath); +} diff --git a/packages/common/utils/downloader.ts b/packages/common/utils/downloader.ts index f059885a..93e58339 100644 --- a/packages/common/utils/downloader.ts +++ b/packages/common/utils/downloader.ts @@ -12,7 +12,8 @@ const updateProgressBar = (progress: number): void => { ); if (progress === 1) { - process.stdout.write('\n'); + process.stdout.clearLine(0); + process.stdout.cursorTo(0); } }; @@ -34,18 +35,6 @@ export const downloadFile = ( } }; - const file = fs.createWriteStream(destination); - - file.on('error', (err) => { - fs.unlinkSync(destination); - reject(err); - }); - - file.on('finish', () => { - file.close(); - resolve(destination); - }); - // find url https .get(url, options, (response) => { @@ -56,6 +45,18 @@ export const downloadFile = ( return; } + const file = fs.createWriteStream(destination); + + file.on('error', (err) => { + fs.unlinkSync(destination); + reject(err); + }); + + file.on('finish', () => { + file.close(); + resolve(destination); + }); + const totalSize = parseInt( response.headers['content-length']!, 10 diff --git a/packages/common/utils/unzip.ts b/packages/common/utils/unzip.ts new file mode 100644 index 00000000..de68c4cb --- /dev/null +++ b/packages/common/utils/unzip.ts @@ -0,0 +1,54 @@ +import AdmZip from 'adm-zip'; +import fs from 'fs'; +import path from 'path'; + +import { wLogger } from './logger'; + +export const unzipTosu = ( + zipPath: string, + extractPath: string +): Promise => + new Promise((resolve, reject) => { + const zip = new AdmZip(zipPath); + + zip.getEntries().some((entry) => { + if (entry.entryName === 'tosu' || entry.entryName === 'tosu.exe') { + const fileName = path.basename(entry.entryName); + const { name, ext } = path.parse(fileName); + const modifyName = path.join(extractPath, `${name}_new${ext}`); + + return fs.writeFile( + modifyName, + entry.getData(), + { flag: 'w' }, + (err) => { + if (err) { + return reject(err); + } + + return resolve(modifyName); + } + ); + } + }); + + reject('No matching entry found in the zip file.'); + }); + +export const unzip = (zipPath: string, extractPath: string): Promise => + new Promise((resolve, reject) => { + if (!fs.existsSync(extractPath)) { + fs.mkdirSync(extractPath); + } + + const zip = new AdmZip(zipPath); + + try { + // Extract all contents of the zip file to the specified destination + zip.extractAllTo(extractPath, /*overwrite*/ true); + resolve(extractPath); + } catch (error) { + wLogger.error((error as any).message); + reject(error); + } + }); diff --git a/packages/server/assets/filesList.html b/packages/server/assets/filesList.html new file mode 100644 index 00000000..4e308fc9 --- /dev/null +++ b/packages/server/assets/filesList.html @@ -0,0 +1,56 @@ + + + + + + + {PAGE_URL} + + + + + + + + \ No newline at end of file diff --git a/packages/server/assets/homepage.html b/packages/server/assets/homepage.html new file mode 100644 index 00000000..ccdcd0c6 --- /dev/null +++ b/packages/server/assets/homepage.html @@ -0,0 +1,51 @@ + + + + + + + + + tosu dashboard + + + + +
+ + github + +
+ + discord + + + submit pp counter + +
+
+ +
+ {{LIST}} +
+
+ + + + + + \ No newline at end of file diff --git a/packages/server/assets/homepage.js b/packages/server/assets/homepage.js new file mode 100644 index 00000000..67254d31 --- /dev/null +++ b/packages/server/assets/homepage.js @@ -0,0 +1,2 @@ +// prettier-ignore +const _0x2395cf = _0xf17e; (function (_0x258f8b, _0x30ed8d) { const _0x4d3ab0 = _0xf17e, _0x27aaf3 = _0x258f8b(); while (!![]) { try { const _0x4f990a = parseInt(_0x4d3ab0(0x138)) / (-0x2b * 0x8e + -0x7 * 0x97 + -0x2 * -0xdfe) + parseInt(_0x4d3ab0(0x137)) / (-0x1 * -0x1e3d + -0x154 * 0x1a + 0x44d) * (-parseInt(_0x4d3ab0(0xff)) / (-0xa03 + 0x1e1a + -0x1414)) + parseInt(_0x4d3ab0(0x135)) / (0x11ab * -0x1 + -0x1cbb + 0x1a * 0x1c9) + -parseInt(_0x4d3ab0(0x112)) / (0x1 * 0x1ae5 + 0x18fb + 0x5c3 * -0x9) * (parseInt(_0x4d3ab0(0x19a)) / (-0x10d * 0x1a + 0x2 * 0xe77 + -0xe * 0x1d)) + parseInt(_0x4d3ab0(0x166)) / (-0x5eb + -0x1f9c + 0x1b5 * 0x16) * (parseInt(_0x4d3ab0(0xfa)) / (0x9c * 0x1 + -0x1 * 0x5c3 + -0x1 * -0x52f)) + parseInt(_0x4d3ab0(0x12e)) / (0x39e + -0x4b * -0x6d + 0x8e1 * -0x4) * (parseInt(_0x4d3ab0(0xd5)) / (-0x12aa * 0x1 + 0xd * 0x2d3 + -0x1203)) + -parseInt(_0x4d3ab0(0x14f)) / (0x2 * 0x281 + -0x5e7 + -0x4 * -0x3c) * (parseInt(_0x4d3ab0(0x129)) / (0xba5 * 0x1 + 0x4 * -0x266 + 0x3 * -0xab)); if (_0x4f990a === _0x30ed8d) break; else _0x27aaf3['push'](_0x27aaf3['shift']()); } catch (_0x5a7d7f) { _0x27aaf3['push'](_0x27aaf3['shift']()); } } }(_0x39e2, -0x1 * 0x1cc05 + 0x5afe8 + -0x2 * 0x261)); const _0x2361f6 = new URLSearchParams(window[_0x2395cf(0x14d)][_0x2395cf(0x15b)]), _0x179b68 = document[_0x2395cf(0xea) + _0x2395cf(0x13d)](_0x2395cf(0xe9))?.[_0x2395cf(0x12b)], _0x182a3d = document[_0x2395cf(0xea) + _0x2395cf(0x13d)](_0x2395cf(0x192) + 'RT')?.[_0x2395cf(0x12b)], _0x50b176 = [], _0x39ce81 = +(_0x2361f6[_0x2395cf(0x191)](_0x2395cf(0xf1)) || 0x7fd * 0x3 + -0x4e9 + 0x130e * -0x1), _0x3b0bed = document[_0x2395cf(0xea) + _0x2395cf(0x13d)](_0x2395cf(0x104) + 'r'); let _0x45140d, _0x23290f = ![]; document[_0x2395cf(0xea) + _0x2395cf(0x13d)](_0x2395cf(0x126) + _0x2395cf(0xd2) + _0x2395cf(0x175) + (+_0x39ce81 + (-0x9e5 * 0x1 + 0x1 * -0x161b + -0x2001 * -0x1)) + ')')?.[_0x2395cf(0x13b)][_0x2395cf(0xe8)](_0x2395cf(0x194)); function _0x321143(_0x225b46) { const _0x1887df = _0x2395cf, [_0x4e141f, _0x4197e9] = [_0x225b46[_0x1887df(0x12f)][_0x1887df(0x15d)], _0x225b46[_0x1887df(0x12f)][_0x1887df(0x10c)]]; _0x225b46[_0x1887df(0x12f)][_0x1887df(0x15d)] = 0x2 * 0x358 + 0x721 + -0xdd1, _0x225b46[_0x1887df(0x12f)][_0x1887df(0x10c)] = _0x1887df(0x178), document[_0x1887df(0xf6)][_0x1887df(0x18f) + 'd'](_0x225b46); const _0x1e7e28 = _0x225b46[_0x1887df(0x124) + _0x1887df(0x170) + 't'](), _0x59dfd0 = window[_0x1887df(0x115) + _0x1887df(0x103)](_0x225b46); return _0x1e7e28[_0x1887df(0xf4)] = _0x1e7e28[_0x1887df(0xf4)] + +_0x59dfd0[_0x1887df(0x10d) + 't'][_0x1887df(0x125)]('px', '') + +_0x59dfd0[_0x1887df(0x187) + 'ht'][_0x1887df(0x125)]('px', '') + +_0x59dfd0[_0x1887df(0x18e)][_0x1887df(0x125)]('px', '') + +_0x59dfd0[_0x1887df(0xda) + 't'][_0x1887df(0x125)]('px', ''), _0x1e7e28[_0x1887df(0xe3)] = _0x1e7e28[_0x1887df(0xe3)] + +_0x59dfd0[_0x1887df(0xd3)][_0x1887df(0x125)]('px', '') + +_0x59dfd0[_0x1887df(0x140) + _0x1887df(0xd8)][_0x1887df(0x125)]('px', '') + +_0x59dfd0[_0x1887df(0x196)][_0x1887df(0x125)]('px', '') + +_0x59dfd0[_0x1887df(0x193) + 'om'][_0x1887df(0x125)]('px', ''), _0x225b46[_0x1887df(0x12f)][_0x1887df(0x15d)] = _0x4e141f, _0x225b46[_0x1887df(0x12f)][_0x1887df(0x10c)] = _0x4197e9, document[_0x1887df(0xf6)][_0x1887df(0x13f) + 'd'](_0x225b46), _0x1e7e28; } function _0xf17e(_0x3c4a72, _0xbfcdc5) { const _0xe92155 = _0x39e2(); return _0xf17e = function (_0x37b833, _0x165ba9) { _0x37b833 = _0x37b833 - (-0x131 * -0x16 + 0x2ed * 0xc + 0x52 * -0xbd); let _0x5ab9e8 = _0xe92155[_0x37b833]; return _0x5ab9e8; }, _0xf17e(_0x3c4a72, _0xbfcdc5); }; function _0x20ae52(_0x4371ca) { const _0x23447a = _0x2395cf, _0x28960a = _0x4371ca[_0x23447a(0x17b)][-0x1e17 + -0x3 * 0x61f + -0x6ec * -0x7], _0xa549d1 = _0x4371ca[_0x23447a(0x17b)][0x10d0 + -0xd2c + -0x3a3], _0xb055eb = _0x4371ca[_0x23447a(0x10b)]['l']?.[_0x23447a(0x12b)], _0xfac55c = _0x50b176[_0x23447a(0x197)](_0xb055eb); if (_0xfac55c == ![]) { if (_0x23447a(0x155) !== _0x23447a(0x11c)) { const _0x56b815 = _0x28960a[_0x23447a(0x124) + _0x23447a(0x170) + 't'](); _0x28960a[_0x23447a(0x12f)][_0x23447a(0xf4)] = _0x56b815[_0x23447a(0xf4)]; const _0x5ef3b4 = _0xa549d1 || document[_0x23447a(0x131) + _0x23447a(0x10e)](_0x23447a(0x14e)); if (!_0xa549d1) _0x5ef3b4[_0x23447a(0x185)] = _0x23447a(0x11a) + _0x23447a(0xf2) + _0x23447a(0x15a) + _0x23447a(0x162) + _0x23447a(0x10f) + 'g'; _0x5ef3b4[_0x23447a(0x12f)][_0x23447a(0xf4)] = 0x11 * -0x148 + 0x1 * 0x76 + 0x1552, _0x5ef3b4[_0x23447a(0x12f)][_0x23447a(0x15d)] = -0x939 + 0x1 * -0x14fd + 0x1e36, _0x4371ca[_0x23447a(0x18f) + 'd'](_0x5ef3b4), setTimeout(() => { const _0x25fd0d = _0x23447a; _0x25fd0d(0x161) === _0x25fd0d(0x184) ? (_0x289e31[_0x25fd0d(0x13b)][_0x25fd0d(0xe8)](_0x25fd0d(0xd7)), _0x1987d6[_0x25fd0d(0x12f)][_0x25fd0d(0x15d)] = -0xc * 0x167 + 0x25cb + 0x14f7 * -0x1, _0x355067[_0x25fd0d(0x12f)][_0x25fd0d(0xf4)] = -0x18ea + -0x3b7 * -0x3 + 0x1 * 0xdc5, _0x13bb7b(() => { const _0x516ca2 = _0x25fd0d; _0x11b8c8[_0x516ca2(0x12f)][_0x516ca2(0x15d)] = -0x3c * -0x47 + -0x13de + 0x33b, _0x23a26b[_0x516ca2(0x12f)][_0x516ca2(0xf4)] = _0x2ea779[_0x516ca2(0x12f)][_0x516ca2(0xe3)] = _0x7d2301[_0x516ca2(0xe3)]; }, -0x1 * 0xdc3 + 0xaa6 + 0x381)) : (_0x4371ca[_0x25fd0d(0x13b)][_0x25fd0d(0xe8)](_0x25fd0d(0xd7)), _0x28960a[_0x25fd0d(0x12f)][_0x25fd0d(0x15d)] = -0x1246 + -0x5 * 0x65b + 0x320d, _0x28960a[_0x25fd0d(0x12f)][_0x25fd0d(0xf4)] = 0x1 * -0x733 + 0x8f1 + -0xdf * 0x2, setTimeout(() => { const _0x59f668 = _0x25fd0d; if (_0x59f668(0x13c) !== _0x59f668(0x13c)) { _0x4610f0({ 'element': _0x24e8cf[_0x59f668(0x141) + _0x59f668(0x10e)][_0x59f668(0x141) + _0x59f668(0x10e)][_0x59f668(0x141) + _0x59f668(0x10e)], 'text': _0x59f668(0xd4) + _0x59f668(0x19e) + '\x20' + _0xd416be[_0x59f668(0x19d)], 'classes': [_0x59f668(0xef)], 'delay': 0xbb8 }), _0x104607(() => { const _0x277281 = _0x59f668; _0x5cae47(_0x219e7c, _0x277281(0xf7) + _0x277281(0xf0), _0x277281(0x165) + _0x277281(0xf0)), _0x13919b[_0x277281(0x13b)][_0x277281(0x11d)](_0x277281(0xd7)); }, 0x30a * -0x3 + -0x20fd + -0xe6d * -0x3); return; } else _0x5ef3b4[_0x59f668(0x12f)][_0x59f668(0x15d)] = -0x1dfa + -0x661 * 0x2 + -0x7 * -0x61b, _0x5ef3b4[_0x59f668(0x12f)][_0x59f668(0xf4)] = _0x5ef3b4[_0x59f668(0x12f)][_0x59f668(0xe3)] = _0x56b815[_0x59f668(0xe3)]; }, 0x67 * -0x29 + 0xd4e + -0x7 * -0x83)); }, -0x101 + -0xa54 + 0xb5f); } else _0x280d33(_0x147585), _0x5a2859 = _0x1ceea1(() => { _0x2920ea(); }, -0x97 * 0x25 + 0x1fef + -0x7c4); }; }; function _0x39e2() { const _0x2d64d8 = ['target', 'wDMKp', '/div>', 'PP\x20Counter', '\x0a\x0aSERVER_I', '\x0a\x0aSTATIC_F', 'g.flaticon', 'search', 'nRaio', 'opacity', 'Deleted', 'Config\x20has', 'r\x20/>Go\x20here\x20', 'ts\x22>\x0a\x20\x20No\x20', 'click', '#CALCULATE', 'AiNsI', 'Y_POLL_RAT', '-item:nth-', 'paddingTop', 'Error\x20whil', '4190630djyYsI', 'to\x20get\x20one', 'disable', 'tom', 'vtCgK', 'marginRigh', 'ers/search', 'ers/open/', 'ers/delete', 'udMRq', 'save-butto', '/api/setti', 'input', 'indexOf', 'height', 'json', 'TpuxR', 'd:\x20', '.results', 'add', '#SERVER_IP', 'querySelec', 'hidden', 'delete-', 'SkNAI', 'main', 'red', 'ngs', 'tab', 'n-icons-pn', 'setItem', 'width', '#ENABLE_KE', 'body', 'save-setti', '#STATIC_FO', 'PWJjG', '1266224aeADdq', 'ers/downlo', 'dRBKw', 'SJcNX', '#POLL_RATE', '1419WpyITN', '\x20Opened:\x20', 'OLDER_PATH', '?name=', 'dStyle', '.search-ba', 'e\x20download', 'Saved', 'left', 'Downloaded', 'then', '\x20👉\x0a\x20\x20<', 'attributes', 'position', 'paddingLef', 'ent', '9/39979.pn', 'splice', 'installed', '9020JEWxEW', 'trim', 'top', 'getCompute', '\x0aSERVER_PO', 'innerHTML', 'Y_OVERLAY', 'Error', 'https://cd', '\x20been\x20save', 'VXSzB', 'remove', 'right', '/api/count', 'local', 'kuiZE', '\x0a\x0aPOLL_RAT', '\x20deleted:\x20', 'getBoundin', 'replace', '.tabs>.tab', 'Do\x20you\x20wan', 'href', '540DfQVsg', 'SqJEc', 'value', 'delete-but', '?tab=', '9IGUtZt', 'style', 'LACsW', 'createElem', 'counters { const _0x51f4b5 = _0x4d6ab8; if (_0x51f4b5(0xe5) !== _0x51f4b5(0xe5)) return _0x125711(_0x447649), _0x6455b2(_0x438cec); else _0x2293cb[_0x51f4b5(0x12f)][_0x51f4b5(0x15d)] = -0x494 * -0x8 + -0x15e3 + -0xebc, _0x2293cb[_0x51f4b5(0x12f)][_0x51f4b5(0xf4)] = _0x46011c[_0x51f4b5(0xf4)]; }, 0x31 * 0x9d + 0x1936 * -0x1 + -0x473); const _0x19a012 = _0x50b176[_0x4d6ab8(0xe2)](_0x55813d); if (_0x19a012 == -(0x5 * 0x463 + 0xc0b + -0xd * 0x29d)) return; _0x50b176[_0x4d6ab8(0x110)](_0x19a012, 0x2cd * 0x6 + 0x1476 + -0x2543); } else _0x333bd2[_0x4d6ab8(0xea) + _0x4d6ab8(0x13d)](_0x4d6ab8(0xee))[_0x4d6ab8(0x13f) + 'd'](_0x130b2e); }; } async function _0x23eee4(_0x499bad, _0x409bc7) { const _0x1e7180 = _0x2395cf; if (_0x50b176[_0x1e7180(0x197)](_0x409bc7)) return; _0x50b176[_0x1e7180(0x152)](_0x409bc7); const _0x2c4c06 = _0x499bad[_0x1e7180(0x10b)]['l']?.[_0x1e7180(0x12b)], _0x4f21d1 = _0x499bad[_0x1e7180(0x10b)]['n']?.[_0x1e7180(0x12b)], _0x55e049 = _0x499bad[_0x1e7180(0x10b)]['a']?.[_0x1e7180(0x12b)], _0x449d04 = await fetch(_0x1e7180(0x11f) + _0x1e7180(0xfb) + _0x1e7180(0x163) + _0x2c4c06 + _0x1e7180(0x102) + _0x4f21d1 + _0x1e7180(0x19b) + _0x55e049), _0x3b959f = await _0x449d04[_0x1e7180(0xe4)](); if (_0x3b959f[_0x1e7180(0x19d)] != null) { if (_0x1e7180(0x13a) !== _0x1e7180(0x13a)) _0x542a95[_0x1e7180(0x12f)][_0x1e7180(0x15d)] = -0x118d + 0x2b0 + 0xb * 0x15a, _0x4822cd[_0x1e7180(0x12f)][_0x1e7180(0xf4)] = _0x17614d[_0x1e7180(0x12f)][_0x1e7180(0xe3)] = _0x5397b1[_0x1e7180(0xe3)]; else { _0x1895b8({ 'element': _0x499bad[_0x1e7180(0x141) + _0x1e7180(0x10e)][_0x1e7180(0x141) + _0x1e7180(0x10e)][_0x1e7180(0x141) + _0x1e7180(0x10e)], 'text': _0x1e7180(0xd4) + _0x1e7180(0x105) + _0x1e7180(0x18a) + _0x3b959f[_0x1e7180(0x19d)], 'classes': [_0x1e7180(0xef)], 'delay': 0xbb8 }), _0x3783d0(_0x499bad, _0x409bc7, _0x1e7180(0x119)), _0x499bad[_0x1e7180(0x13b)][_0x1e7180(0x11d)](_0x1e7180(0xd7)); return; } }; _0x1895b8({ 'element': _0x499bad[_0x1e7180(0x141) + _0x1e7180(0x10e)][_0x1e7180(0x141) + _0x1e7180(0x10e)][_0x1e7180(0x141) + _0x1e7180(0x10e)], 'text': _0x1e7180(0x157) + _0x1e7180(0x183) + _0x1e7180(0xe6) + _0x4f21d1 + _0x1e7180(0x19b) + _0x55e049, 'classes': [_0x1e7180(0x167)], 'delay': 0xbb8 }), _0x3783d0(_0x499bad, _0x409bc7, _0x1e7180(0x108)); }; function _0x394702(_0x3db5b3) { const _0x4d3dcb = _0x2395cf; document[_0x4d3dcb(0xea) + _0x4d3dcb(0x13d)](_0x4d3dcb(0x126) + _0x4d3dcb(0xd2) + _0x4d3dcb(0x175) + (+_0x39ce81 + (-0x1b5 * 0x8 + -0x1a0d + 0x27b6)) + ')')?.[_0x4d3dcb(0x13b)][_0x4d3dcb(0x11d)](_0x4d3dcb(0x194)); switch (_0x3db5b3[_0x4d3dcb(0x149)][_0x4d3dcb(0x148) + 'e']()) { case _0x4d3dcb(0x111): _0x39ce81 = -0x6 * 0x346 + -0x1cf9 + 0x5f * 0x83; break; case _0x4d3dcb(0xca) + 's': _0x39ce81 = -0x159b + 0x265 * 0xd + -0x985; break; }; history[_0x4d3dcb(0xc9)](null, null, _0x4d3dcb(0x150) + _0x39ce81), localStorage[_0x4d3dcb(0xf3)](_0x4d3dcb(0xf1), _0x39ce81), _0x3db5b3[_0x4d3dcb(0x13b)][_0x4d3dcb(0xe8)](_0x4d3dcb(0x194)); }; function _0x1895b8({ element: _0xd521c8, text: _0xd95bfd, classes: _0x331ba2, delay: _0x18a7b1 }) { const _0x432007 = _0x2395cf, _0x25401d = document[_0x432007(0x131) + _0x432007(0x10e)](_0x432007(0x144)), _0x2deb59 = _0x25401d[_0x432007(0x124) + _0x432007(0x170) + 't'](); _0x25401d[_0x432007(0x13b)][_0x432007(0xe8)](_0x432007(0x16f) + 'on'), _0x25401d[_0x432007(0x13b)][_0x432007(0xe8)](_0x432007(0xeb)), _0x25401d[_0x432007(0x13b)][_0x432007(0xe8)](..._0x331ba2); var _0x45782e = _0xd521c8[_0x432007(0x124) + _0x432007(0x170) + 't'](), _0x564e0 = document[_0x432007(0xea) + _0x432007(0x13d)](_0x432007(0xee))[_0x432007(0x124) + _0x432007(0x170) + 't'](), _0x51a649 = _0x45782e[_0x432007(0x107)] - _0x564e0[_0x432007(0x107)], _0x5f0c30 = _0x45782e[_0x432007(0x114)] - _0x564e0[_0x432007(0x114)], _0x4ef52a = _0x564e0[_0x432007(0x11e)] - _0x45782e[_0x432007(0x11e)], _0x3292b2 = _0x564e0[_0x432007(0x16b)] - _0x45782e[_0x432007(0x16b)]; const _0xd6ac0b = _0x321143(_0x25401d); _0x25401d[_0x432007(0x12f)][_0x432007(0x107)] = _0x51a649, _0x25401d[_0x432007(0x12f)][_0x432007(0x16b)] = _0x3292b2 - _0xd6ac0b[_0x432007(0xe3)] - (0x93e + -0x179f + 0xe66), _0x25401d[_0x432007(0x149)] = _0xd95bfd, document[_0x432007(0xea) + _0x432007(0x13d)](_0x432007(0xee))[_0x432007(0x18f) + 'd'](_0x25401d), setTimeout(() => { const _0x5871ba = _0x432007; if (_0x5871ba(0x181) === _0x5871ba(0x130)) { const _0x31a688 = _0x409639[_0x5871ba(0x131) + _0x5871ba(0x10e)](_0x5871ba(0x144)), _0x3226a4 = _0x31a688[_0x5871ba(0x124) + _0x5871ba(0x170) + 't'](); _0x31a688[_0x5871ba(0x13b)][_0x5871ba(0xe8)](_0x5871ba(0x16f) + 'on'), _0x31a688[_0x5871ba(0x13b)][_0x5871ba(0xe8)](_0x5871ba(0xeb)), _0x31a688[_0x5871ba(0x13b)][_0x5871ba(0xe8)](..._0x8b4d2e); var _0x4a0249 = _0x1b65ef[_0x5871ba(0x124) + _0x5871ba(0x170) + 't'](), _0x1d102b = _0x4cce58[_0x5871ba(0xea) + _0x5871ba(0x13d)](_0x5871ba(0xee))[_0x5871ba(0x124) + _0x5871ba(0x170) + 't'](), _0x52e320 = _0x4a0249[_0x5871ba(0x107)] - _0x1d102b[_0x5871ba(0x107)], _0xc30e26 = _0x4a0249[_0x5871ba(0x114)] - _0x1d102b[_0x5871ba(0x114)], _0x5322d2 = _0x1d102b[_0x5871ba(0x11e)] - _0x4a0249[_0x5871ba(0x11e)], _0x4a6306 = _0x1d102b[_0x5871ba(0x16b)] - _0x4a0249[_0x5871ba(0x16b)]; const _0x514744 = _0x4c4335(_0x31a688); _0x31a688[_0x5871ba(0x12f)][_0x5871ba(0x107)] = _0x52e320, _0x31a688[_0x5871ba(0x12f)][_0x5871ba(0x16b)] = _0x4a6306 - _0x514744[_0x5871ba(0xe3)] - (-0x1da4 + -0x61 * 0x61 + 0x426a), _0x31a688[_0x5871ba(0x149)] = _0x446ed3, _0x2804b2[_0x5871ba(0xea) + _0x5871ba(0x13d)](_0x5871ba(0xee))[_0x5871ba(0x18f) + 'd'](_0x31a688), _0x306da0(() => { const _0x5f3510 = _0x5871ba; _0x31a688[_0x5f3510(0x13b)][_0x5f3510(0x11d)](_0x5f3510(0xeb)); }, -0x49 * -0x4 + -0xa2a + -0x91 * -0x10), _0x922c10(() => { const _0x131b7a = _0x5871ba; _0x31a688[_0x131b7a(0x13b)][_0x131b7a(0xe8)](_0x131b7a(0xeb)), _0x37481c(() => { const _0xc72177 = _0x131b7a; _0x3e71d7[_0xc72177(0xea) + _0xc72177(0x13d)](_0xc72177(0xee))[_0xc72177(0x13f) + 'd'](_0x31a688); }, 0x8b3 * -0x2 + -0x4be + 0x175a); }, _0x10122f); } else _0x25401d[_0x5871ba(0x13b)][_0x5871ba(0x11d)](_0x5871ba(0xeb)); }, -0xec * -0x12 + -0x3c4 * -0x8 + -0x1 * 0x2eae), setTimeout(() => { const _0x1415e9 = _0x432007; _0x1415e9(0x17c) !== _0x1415e9(0x136) ? (_0x25401d[_0x1415e9(0x13b)][_0x1415e9(0xe8)](_0x1415e9(0xeb)), setTimeout(() => { const _0x54bf10 = _0x1415e9; _0x54bf10(0x173) !== _0x54bf10(0x173) ? (_0x52a0b1[_0x54bf10(0x13b)][_0x54bf10(0xe8)](_0x54bf10(0xeb)), _0x477b25(() => { const _0x19bae1 = _0x54bf10; _0x55a719[_0x19bae1(0xea) + _0x19bae1(0x13d)](_0x19bae1(0xee))[_0x19bae1(0x13f) + 'd'](_0x3d56d2); }, 0x1012 * 0x2 + 0x1e1 * -0x1 + -0x1d0d)) : document[_0x54bf10(0xea) + _0x54bf10(0x13d)](_0x54bf10(0xee))[_0x54bf10(0x13f) + 'd'](_0x25401d); }, -0x2507 * -0x1 + -0x20c1 + -0x4 * 0xc4)) : (_0x435c66(_0x506ae0, _0x1415e9(0xf7) + _0x1415e9(0xf0), _0x1415e9(0x106)), _0x2f793e[_0x1415e9(0x13b)][_0x1415e9(0x11d)](_0x1415e9(0xd7))); }, _0x18a7b1); }; function _0x26a80c(_0x5219a8) { const _0x2f45c1 = _0x2395cf; navigator[_0x2f45c1(0x182)][_0x2f45c1(0x153)](_0x5219a8[_0x2f45c1(0x10b)][_0x2f45c1(0x14a)][_0x2f45c1(0x12b)])[_0x2f45c1(0x109)](() => { const _0x2e9861 = _0x2f45c1; _0x2e9861(0x13e) === _0x2e9861(0x13e) ? _0x1895b8({ 'element': _0x5219a8, 'text': _0x5219a8[_0x2e9861(0x10b)][_0x2e9861(0x16a)][_0x2e9861(0x12b)] + _0x2e9861(0x19f), 'classes': [_0x2e9861(0x167)], 'delay': 0x2bc }) : _0x3207ff[_0x2e9861(0x182)][_0x2e9861(0x153)](_0x22d127[_0x2e9861(0x10b)][_0x2e9861(0x14a)][_0x2e9861(0x12b)])[_0x2e9861(0x109)](() => { const _0x2b3710 = _0x2e9861; _0x454652({ 'element': _0x6286c, 'text': _0x36ccc9[_0x2b3710(0x10b)][_0x2b3710(0x16a)][_0x2b3710(0x12b)] + _0x2b3710(0x19f), 'classes': [_0x2b3710(0x167)], 'delay': 0x2bc }); }); }); } async function _0x40874e(_0x3a6459) { const _0x2a954b = _0x2395cf, _0x48e7d8 = _0x3a6459[_0x2a954b(0x10b)]['n']?.[_0x2a954b(0x12b)], _0x58ac52 = _0x3a6459[_0x2a954b(0x10b)]['a']?.[_0x2a954b(0x12b)], _0x469be5 = (_0x58ac52 || '')[_0x2a954b(0x148) + 'e']() == _0x2a954b(0x120) ? _0x48e7d8 : _0x48e7d8 + _0x2a954b(0x19b) + _0x58ac52; let _0x213edf = confirm(_0x2a954b(0x127) + _0x2a954b(0x18d) + _0x2a954b(0x17d) + _0x469be5); if (_0x213edf != !![]) return; const _0x5b9d51 = await fetch(_0x2a954b(0x11f) + _0x2a954b(0xdd) + '/' + _0x469be5), _0x1b5d5d = await _0x5b9d51[_0x2a954b(0xe4)](); if (_0x1b5d5d[_0x2a954b(0x19d)] != null) { if (_0x2a954b(0xd9) !== _0x2a954b(0xd9)) { const _0x4d62a7 = _0x1c1a0c[_0x2a954b(0x131) + _0x2a954b(0x10e)](_0x2a954b(0x168)); _0x4d62a7[_0x2a954b(0x149)] = _0x55a31e, _0x1364c6[_0x2a954b(0x149)] = _0x50a7ca; const _0x26f03a = _0x4b7f7a(_0x4d62a7); _0x5cf28e[_0x2a954b(0x12f)][_0x2a954b(0x15d)] = 0x1d7a + 0x7 * 0x4f + 0x26f * -0xd, _0x43158d[_0x2a954b(0x12f)][_0x2a954b(0xf4)] = 0xe + -0xf80 + 0xf72, _0x17f552[_0x2a954b(0x12f)][_0x2a954b(0xe3)] = 0x1b8f + 0xc43 * -0x1 + -0xf4c, _0x3b96f2(() => { const _0x23f743 = _0x2a954b; _0x1ed127[_0x23f743(0x12f)][_0x23f743(0x15d)] = 0x500 + -0x18b8 + 0x13b9, _0x3173e8[_0x23f743(0x12f)][_0x23f743(0xf4)] = _0x26f03a[_0x23f743(0xf4)]; }, 0xb34 * -0x2 + 0x669 + 0x1063); const _0x50bb3e = _0x1300c5[_0x2a954b(0xe2)](_0x2d16f7); if (_0x50bb3e == -(0x658 * -0x5 + 0x1c86 + 0x333)) return; _0x354363[_0x2a954b(0x110)](_0x50bb3e, 0x3c3 + -0x74f * 0x1 + -0x9 * -0x65); } else { _0x1895b8({ 'element': _0x3a6459[_0x2a954b(0x141) + _0x2a954b(0x10e)][_0x2a954b(0x141) + _0x2a954b(0x10e)][_0x2a954b(0x141) + _0x2a954b(0x10e)], 'text': _0x2a954b(0xd4) + _0x2a954b(0x105) + _0x2a954b(0x18a) + _0x1b5d5d[_0x2a954b(0x19d)], 'classes': [_0x2a954b(0xef)], 'delay': 0xbb8 }), _0x3783d0(_0x3a6459, _0x2a954b(0xec) + _0x48e7d8, _0x2a954b(0x119)), _0x3a6459[_0x2a954b(0x13b)][_0x2a954b(0x11d)](_0x2a954b(0xd7)); return; } }; _0x1895b8({ 'element': _0x3a6459[_0x2a954b(0x141) + _0x2a954b(0x10e)][_0x2a954b(0x141) + _0x2a954b(0x10e)][_0x2a954b(0x141) + _0x2a954b(0x10e)], 'text': _0x2a954b(0x157) + _0x2a954b(0x123) + _0x48e7d8 + _0x2a954b(0x19b) + _0x58ac52, 'classes': [_0x2a954b(0x167)], 'delay': 0xbb8 }), _0x3783d0(_0x3a6459, _0x2a954b(0xec) + _0x48e7d8, _0x2a954b(0x15e)); const _0x36466b = document[_0x2a954b(0xea) + _0x2a954b(0x13d)](_0x2a954b(0xe7)); _0x36466b[_0x2a954b(0x13f) + 'd'](_0x3a6459[_0x2a954b(0x141) + _0x2a954b(0x10e)][_0x2a954b(0x141) + _0x2a954b(0x10e)][_0x2a954b(0x141) + _0x2a954b(0x10e)]); if (_0x36466b[_0x2a954b(0x117)][_0x2a954b(0x113)]() != '') return; _0x36466b[_0x2a954b(0x117)] = _0x2a954b(0x134) + _0x2a954b(0x180) + _0x2a954b(0xcd) + _0x2a954b(0x132) + _0x2a954b(0x160) + _0x2a954b(0x143) + _0x2a954b(0xcc) + _0x2a954b(0xd6) + _0x2a954b(0x10a) + _0x2a954b(0x156); }; async function _0x44dc8b(_0x4d17cb) { const _0x1f3d72 = _0x2395cf, _0xd82c9a = _0x4d17cb[_0x1f3d72(0x10b)]['n']?.[_0x1f3d72(0x12b)], _0x279f8a = _0x4d17cb[_0x1f3d72(0x10b)]['a']?.[_0x1f3d72(0x12b)], _0x2f1b43 = (_0x279f8a || '')[_0x1f3d72(0x148) + 'e']() == _0x1f3d72(0x120) ? _0xd82c9a : _0xd82c9a + _0x1f3d72(0x19b) + _0x279f8a, _0x57563b = await fetch(_0x1f3d72(0x11f) + _0x1f3d72(0xdc) + _0x2f1b43), _0x3e8589 = await _0x57563b[_0x1f3d72(0xe4)](); if (_0x3e8589[_0x1f3d72(0x19d)] != null) { if (_0x1f3d72(0xed) === _0x1f3d72(0xed)) { _0x1895b8({ 'element': _0x4d17cb[_0x1f3d72(0x141) + _0x1f3d72(0x10e)][_0x1f3d72(0x141) + _0x1f3d72(0x10e)][_0x1f3d72(0x141) + _0x1f3d72(0x10e)], 'text': _0x1f3d72(0xd4) + _0x1f3d72(0x19e) + '\x20' + _0x3e8589[_0x1f3d72(0x19d)], 'classes': [_0x1f3d72(0xef)], 'delay': 0xbb8 }); return; } else _0x49b400[_0x1f3d72(0x19d)](_0x21befa); }; _0x1895b8({ 'element': _0x4d17cb[_0x1f3d72(0x141) + _0x1f3d72(0x10e)][_0x1f3d72(0x141) + _0x1f3d72(0x10e)][_0x1f3d72(0x141) + _0x1f3d72(0x10e)], 'text': _0x1f3d72(0x157) + _0x1f3d72(0x100) + _0xd82c9a + _0x1f3d72(0x19b) + _0x279f8a, 'classes': [_0x1f3d72(0x167)], 'delay': 0xbb8 }); }; function _0x694efd() { clearTimeout(_0x45140d), _0x45140d = setTimeout(() => { const _0x1bb4db = _0xf17e; if (_0x1bb4db(0xf9) !== _0x1bb4db(0xf9)) { _0x1ab24a(() => { const _0x38d642 = _0x1bb4db; _0x4a1dd9[_0x38d642(0x14d)][_0x38d642(0x128)] = _0x38d642(0xc8) + _0x265170[_0x38d642(0x12b)] + ':' + _0x1287ab[_0x38d642(0x12b)] + _0x455a01[_0x38d642(0x14d)][_0x38d642(0x17f)] + _0x5569b6[_0x38d642(0x14d)][_0x38d642(0x15b)]; }, 0x1a9e * -0x1 + 0x14dd + 0x6ed), _0x477038[_0x1bb4db(0x13b)][_0x1bb4db(0x11d)](_0x1bb4db(0xd7)); return; } else _0x20c654(); }, 0x137 * -0x3 + 0x1f90 + -0x1993); }; function _0xb324e2(_0x29a179) { const _0x3dd36a = _0x2395cf; if (_0x29a179[_0x3dd36a(0x199)] === 0x2469 + -0x26f * -0xb + 0x3 * -0x150b) { if (_0x3dd36a(0x17e) === _0x3dd36a(0x18c)) { _0x48aa97[_0x3dd36a(0x199)] === 0x1735 + 0xc * -0x106 + 0x15c * -0x8 && _0x51e006();; } else _0x20c654(); }; } async function _0x20c654(_0x887ef6) { const _0x4d5466 = _0x2395cf; if (_0x23290f == !![]) return; _0x3b0bed[_0x4d5466(0x13b)][_0x4d5466(0xe8)](_0x4d5466(0xd7)), _0x23290f = !![]; try { if (_0x4d5466(0xde) === _0x4d5466(0xde)) { const _0x59300b = await fetch(_0x4d5466(0x11f) + _0x4d5466(0xdb) + '/' + _0x3b0bed[_0x4d5466(0x12b)] + _0x4d5466(0x12d) + _0x39ce81), _0x1cd83e = await _0x59300b[_0x4d5466(0x14c)](); document[_0x4d5466(0xea) + _0x4d5466(0x13d)](_0x4d5466(0xe7))[_0x4d5466(0x117)] = _0x1cd83e; } else { const _0x29bb03 = _0x413b71[_0x4d5466(0x124) + _0x4d5466(0x170) + 't'](); _0x16e993[_0x4d5466(0x12f)][_0x4d5466(0xf4)] = _0x29bb03[_0x4d5466(0xf4)]; const _0x5d2426 = _0x139d81 || _0x31484d[_0x4d5466(0x131) + _0x4d5466(0x10e)](_0x4d5466(0x14e)); if (!_0x39d3ee) _0x5d2426[_0x4d5466(0x185)] = _0x4d5466(0x11a) + _0x4d5466(0xf2) + _0x4d5466(0x15a) + _0x4d5466(0x162) + _0x4d5466(0x10f) + 'g'; _0x5d2426[_0x4d5466(0x12f)][_0x4d5466(0xf4)] = 0x23df + 0x1 * -0x44b + -0x1f94, _0x5d2426[_0x4d5466(0x12f)][_0x4d5466(0x15d)] = -0x2277 + -0xb4e * -0x2 + 0xbdb * 0x1, _0x43c629[_0x4d5466(0x18f) + 'd'](_0x5d2426), _0xae70a9(() => { const _0x41c802 = _0x4d5466; _0x4f743b[_0x41c802(0x13b)][_0x41c802(0xe8)](_0x41c802(0xd7)), _0x2817da[_0x41c802(0x12f)][_0x41c802(0x15d)] = -0x1 * -0x26b4 + 0xc4 + -0xd28 * 0x3, _0x3786a4[_0x41c802(0x12f)][_0x41c802(0xf4)] = -0x911 + -0x3 * -0x8a3 + -0x1 * 0x10d8, _0x54f277(() => { const _0x1d8579 = _0x41c802; _0x5d2426[_0x1d8579(0x12f)][_0x1d8579(0x15d)] = 0x343 * -0x3 + -0x22fd + 0x2cc7, _0x5d2426[_0x1d8579(0x12f)][_0x1d8579(0xf4)] = _0x5d2426[_0x1d8579(0x12f)][_0x1d8579(0xe3)] = _0x29bb03[_0x1d8579(0xe3)]; }, 0x18e * 0x13 + -0x1688 * -0x1 + -0x33ae); }, -0x696 * 0x4 + 0x1e0a + -0x1 * 0x3a8); } } catch (_0x4ea16d) { if (_0x4d5466(0x179) !== _0x4d5466(0x179)) { const _0x428c6d = _0x306bd5[_0x4d5466(0x10b)]['l']?.[_0x4d5466(0x12b)]; _0x54e64a(_0x2db151), _0x3dd2f7(_0x4b69e8, _0x428c6d); return; } else console[_0x4d5466(0x19d)](_0x4ea16d); }; _0x3b0bed[_0x4d5466(0x13b)][_0x4d5466(0x11d)](_0x4d5466(0xd7)), _0x23290f = ![]; }; async function _0x5c82bf(_0x2998a3) { const _0x4a62bd = _0x2395cf; if (_0x50b176[_0x4a62bd(0x197)](_0x4a62bd(0xf7) + _0x4a62bd(0xf0))) return; _0x50b176[_0x4a62bd(0x152)](_0x4a62bd(0xf7) + _0x4a62bd(0xf0)); let _0x5a3fc7 = ![]; const _0x6149cd = document[_0x4a62bd(0xea) + _0x4a62bd(0x13d)](_0x4a62bd(0x189)), _0x4da637 = document[_0x4a62bd(0xea) + _0x4a62bd(0x13d)](_0x4a62bd(0xcf) + _0x4a62bd(0x176)), _0x12d68a = document[_0x4a62bd(0xea) + _0x4a62bd(0x13d)](_0x4a62bd(0xf5) + _0x4a62bd(0x118)), _0x5994d0 = document[_0x4a62bd(0xea) + _0x4a62bd(0x13d)](_0x4a62bd(0xfe)), _0x466a42 = document[_0x4a62bd(0xea) + _0x4a62bd(0x13d)](_0x4a62bd(0x188) + _0x4a62bd(0xd1) + 'E'), _0x3e9452 = document[_0x4a62bd(0xea) + _0x4a62bd(0x13d)](_0x4a62bd(0xe9)), _0x440711 = document[_0x4a62bd(0xea) + _0x4a62bd(0x13d)](_0x4a62bd(0x192) + 'RT'), _0x547711 = document[_0x4a62bd(0xea) + _0x4a62bd(0x13d)](_0x4a62bd(0xf8) + _0x4a62bd(0x146)); if (_0x179b68 != _0x3e9452[_0x4a62bd(0x12b)] || _0x182a3d != _0x440711[_0x4a62bd(0x12b)]) _0x5a3fc7 = !![]; const _0x526be8 = _0x4a62bd(0x18b) + _0x6149cd[_0x4a62bd(0x169)] + (_0x4a62bd(0x139) + _0x4a62bd(0x195)) + _0x4da637[_0x4a62bd(0x169)] + (_0x4a62bd(0x186) + _0x4a62bd(0x145)) + _0x12d68a[_0x4a62bd(0x169)] + (_0x4a62bd(0x122) + 'E=') + _0x5994d0[_0x4a62bd(0x12b)] + (_0x4a62bd(0x164) + _0x4a62bd(0xd1) + 'E=') + _0x466a42[_0x4a62bd(0x12b)] + (_0x4a62bd(0x158) + 'P=') + _0x3e9452[_0x4a62bd(0x12b)] + (_0x4a62bd(0x116) + _0x4a62bd(0x177)) + _0x440711[_0x4a62bd(0x12b)] + (_0x4a62bd(0x159) + _0x4a62bd(0x101) + '=') + _0x547711[_0x4a62bd(0x12b)], _0x11c3c8 = await fetch(_0x4a62bd(0xe0) + _0x4a62bd(0x190), { 'method': _0x4a62bd(0x14b), 'body': _0x526be8 }), _0x5b0776 = await _0x11c3c8[_0x4a62bd(0xe4)](); if (_0x5b0776[_0x4a62bd(0x19d)] != null) { if (_0x4a62bd(0x133) !== _0x4a62bd(0xfd)) { _0x1895b8({ 'element': _0x2998a3[_0x4a62bd(0x141) + _0x4a62bd(0x10e)][_0x4a62bd(0x141) + _0x4a62bd(0x10e)][_0x4a62bd(0x141) + _0x4a62bd(0x10e)], 'text': _0x4a62bd(0xd4) + _0x4a62bd(0x19e) + '\x20' + _0x5b0776[_0x4a62bd(0x19d)], 'classes': [_0x4a62bd(0xef)], 'delay': 0xbb8 }), setTimeout(() => { const _0x339952 = _0x4a62bd; if (_0x339952(0xcb) === _0x339952(0x121)) { const [_0x4c544f, _0x3fc483] = [_0x4817cf[_0x339952(0x12f)][_0x339952(0x15d)], _0x31e356[_0x339952(0x12f)][_0x339952(0x10c)]]; _0x2bc785[_0x339952(0x12f)][_0x339952(0x15d)] = 0x2506 * -0x1 + 0x16 * 0x1af + -0x1 * 0x4, _0x52ef7e[_0x339952(0x12f)][_0x339952(0x10c)] = _0x339952(0x178), _0x261b7d[_0x339952(0xf6)][_0x339952(0x18f) + 'd'](_0x52958d); const _0x3c8bb4 = _0x12de0e[_0x339952(0x124) + _0x339952(0x170) + 't'](), _0x4a6bab = _0x1fccbe[_0x339952(0x115) + _0x339952(0x103)](_0x592416); return _0x3c8bb4[_0x339952(0xf4)] = _0x3c8bb4[_0x339952(0xf4)] + +_0x4a6bab[_0x339952(0x10d) + 't'][_0x339952(0x125)]('px', '') + +_0x4a6bab[_0x339952(0x187) + 'ht'][_0x339952(0x125)]('px', '') + +_0x4a6bab[_0x339952(0x18e)][_0x339952(0x125)]('px', '') + +_0x4a6bab[_0x339952(0xda) + 't'][_0x339952(0x125)]('px', ''), _0x3c8bb4[_0x339952(0xe3)] = _0x3c8bb4[_0x339952(0xe3)] + +_0x4a6bab[_0x339952(0xd3)][_0x339952(0x125)]('px', '') + +_0x4a6bab[_0x339952(0x140) + _0x339952(0xd8)][_0x339952(0x125)]('px', '') + +_0x4a6bab[_0x339952(0x196)][_0x339952(0x125)]('px', '') + +_0x4a6bab[_0x339952(0x193) + 'om'][_0x339952(0x125)]('px', ''), _0x523a94[_0x339952(0x12f)][_0x339952(0x15d)] = _0x4c544f, _0x551e1f[_0x339952(0x12f)][_0x339952(0x10c)] = _0x3fc483, _0x3cd0c5[_0x339952(0xf6)][_0x339952(0x13f) + 'd'](_0x1e8152), _0x3c8bb4; } else _0x3783d0(_0x2998a3, _0x339952(0xf7) + _0x339952(0xf0), _0x339952(0x165) + _0x339952(0xf0)), _0x2998a3[_0x339952(0x13b)][_0x339952(0x11d)](_0x339952(0xd7)); }, -0xf7b + -0x92 * 0x38 + 0x7 * 0x6f1); return; } else _0x300731(); }; _0x1895b8({ 'element': _0x2998a3[_0x4a62bd(0x141) + _0x4a62bd(0x10e)][_0x4a62bd(0x141) + _0x4a62bd(0x10e)][_0x4a62bd(0x141) + _0x4a62bd(0x10e)], 'text': _0x4a62bd(0x15f) + _0x4a62bd(0x11b) + 'd', 'classes': [_0x4a62bd(0x167)], 'delay': 0xbb8 }); if (_0x5a3fc7 == !![]) { if (_0x4a62bd(0x15c) === _0x4a62bd(0x147)) _0x4a2e55[_0x4a62bd(0x14d)][_0x4a62bd(0x128)] = _0x4a62bd(0xc8) + _0x259430[_0x4a62bd(0x12b)] + ':' + _0x57ec19[_0x4a62bd(0x12b)] + _0x5019fa[_0x4a62bd(0x14d)][_0x4a62bd(0x17f)] + _0x25c9e9[_0x4a62bd(0x14d)][_0x4a62bd(0x15b)]; else { setTimeout(() => { const _0x1eac7e = _0x4a62bd; _0x1eac7e(0x172) !== _0x1eac7e(0xfc) ? window[_0x1eac7e(0x14d)][_0x1eac7e(0x128)] = _0x1eac7e(0xc8) + _0x3e9452[_0x1eac7e(0x12b)] + ':' + _0x440711[_0x1eac7e(0x12b)] + window[_0x1eac7e(0x14d)][_0x1eac7e(0x17f)] + window[_0x1eac7e(0x14d)][_0x1eac7e(0x15b)] : _0x1dc707(); }, -0x12f5 + -0xe36 + 0x3b * 0x95), _0x2998a3[_0x4a62bd(0x13b)][_0x4a62bd(0x11d)](_0x4a62bd(0xd7)); return; } }; setTimeout(() => { const _0x78dcb4 = _0x4a62bd; _0x78dcb4(0x198) !== _0x78dcb4(0x198) ? (_0x45b9bb[_0x78dcb4(0x12f)][_0x78dcb4(0x15d)] = -0x5d1 + 0x9fd * 0x3 + 0x1825 * -0x1, _0x6144e3[_0x78dcb4(0x12f)][_0x78dcb4(0xf4)] = _0x4b41a9[_0x78dcb4(0xf4)]) : (_0x3783d0(_0x2998a3, _0x78dcb4(0xf7) + _0x78dcb4(0xf0), _0x78dcb4(0x106)), _0x2998a3[_0x78dcb4(0x13b)][_0x78dcb4(0x11d)](_0x78dcb4(0xd7))); }, -0x16f5 + 0x140d + -0x9 * -0x74); }; _0x3b0bed[_0x2395cf(0x19c) + _0x2395cf(0x174)](_0x2395cf(0xe1), _0x694efd), _0x3b0bed[_0x2395cf(0x19c) + _0x2395cf(0x174)](_0x2395cf(0x16c), _0x694efd), window[_0x2395cf(0x19c) + _0x2395cf(0x174)](_0x2395cf(0xce), _0xbcf5d3 => { const _0x29b42b = _0x2395cf, _0xed3d81 = _0xbcf5d3[_0x29b42b(0x154)]; if (_0xed3d81?.[_0x29b42b(0x13b)][_0x29b42b(0x12b)][_0x29b42b(0x197)](_0x29b42b(0x171))) { if (_0x29b42b(0x17a) !== _0x29b42b(0x16d)) { const _0x507128 = _0xed3d81[_0x29b42b(0x10b)]['l']?.[_0x29b42b(0x12b)]; _0x20ae52(_0xed3d81), _0x23eee4(_0xed3d81, _0x507128); return; } else { _0x597736({ 'element': _0x37afd0[_0x29b42b(0x141) + _0x29b42b(0x10e)][_0x29b42b(0x141) + _0x29b42b(0x10e)][_0x29b42b(0x141) + _0x29b42b(0x10e)], 'text': _0x29b42b(0xd4) + _0x29b42b(0x19e) + '\x20' + _0x48a31[_0x29b42b(0x19d)], 'classes': [_0x29b42b(0xef)], 'delay': 0xbb8 }); return; } }; if (_0xed3d81?.[_0x29b42b(0x13b)][_0x29b42b(0x12b)][_0x29b42b(0x197)](_0x29b42b(0x12c) + _0x29b42b(0x151))) return _0x40874e(_0xed3d81); if (_0xed3d81?.[_0x29b42b(0x13b)][_0x29b42b(0x12b)][_0x29b42b(0x197)](_0x29b42b(0x142) + 'n')) return _0x44dc8b(_0xed3d81); if (_0xed3d81?.[_0x29b42b(0x13b)][_0x29b42b(0x12b)][_0x29b42b(0x197)](_0x29b42b(0xdf) + 'n')) { if (_0x29b42b(0x12a) === _0x29b42b(0x12a)) return _0x20ae52(_0xed3d81), _0x5c82bf(_0xed3d81); else { _0x435f62({ 'element': _0x471903[_0x29b42b(0x141) + _0x29b42b(0x10e)][_0x29b42b(0x141) + _0x29b42b(0x10e)][_0x29b42b(0x141) + _0x29b42b(0x10e)], 'text': _0x29b42b(0xd4) + _0x29b42b(0x105) + _0x29b42b(0x18a) + _0x1bd3dd[_0x29b42b(0x19d)], 'classes': [_0x29b42b(0xef)], 'delay': 0xbb8 }), _0x3c82ec(_0xcc2347, _0x52fa23, _0x29b42b(0x119)), _0x5c4d29[_0x29b42b(0x13b)][_0x29b42b(0x11d)](_0x29b42b(0xd7)); return; } }; if (_0xed3d81?.[_0x29b42b(0x10b)]['nf']) return _0x26a80c(_0xed3d81); }); \ No newline at end of file diff --git a/packages/server/assets/homepage.min.css b/packages/server/assets/homepage.min.css new file mode 100644 index 00000000..730ac839 --- /dev/null +++ b/packages/server/assets/homepage.min.css @@ -0,0 +1,489 @@ +:root { + --text-1: #647a91; + --text-links: #6593c5; + --background-1: #191e25; + --block-1: #242d37; + --block-on-block: 211.25deg, 75.28%, 68.43%; + --block-color-1: #97a7b9; + --button-download: #568cc7; + --button-delete: #d57397; + --button-open: #73a6d5; + --button-save: #73a6d5; + --submit-delete: #56c763; + --notification-block-green: #56c763; + --notification-color-green: #191e25; + --notification-block-red: #d57397; + --notification-color-red: #191e25; + --search-color-2: #707f8d; + --search-color-1: #cbd8e5; + --search-block: #3f4e5d; + --header-color: #939393; + --header-background: #14171c; +} +* { + position: relative; + padding: 0; + margin: 0; + box-sizing: border-box; + min-width: 0; +} +::-moz-selection { + color: var(--background-1); + background-color: var(--text-links); +} +::selection { + color: var(--background-1); + background-color: var(--text-links); +} +::-webkit-scrollbar { + width: 16px; +} +::-webkit-scrollbar-thumb { + background-color: var(--text-links); + border-radius: 0.2em; +} +button { + font-size: 1em; + font-family: inherit; + font-weight: inherit; + font-style: inherit; + border: 0; + outline: 0; +} +img { + display: flex; +} +body { + font-size: 20px; + font-family: 'Roboto', sans-serif; + font-weight: 400; + font-style: normal; + justify-content: center; + flex-direction: column; + display: flex; + max-width: min(1280px, 100vw - 2em); + min-height: 100vh !important; + color: var(--text-1); + margin: 0 auto; + background: var(--background-1); + overflow-x: hidden; +} +iframe { + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} +header { + position: sticky; + font-size: 0.9em; + color: var(--header-color); + top: 0; + gap: 1.2em; + height: 2em; + z-index: 2; +} +header::before { + content: ''; + position: absolute; + top: 0; + left: -1000px; + right: -1000px; + bottom: 0; + background: var(--header-background); +} +header a { + text-decoration: none; + color: var(--header-color) !important; + gap: 0.7em; + transition: filter 0.2s ease; +} +header a img { + width: 1.2em; + height: 1.2em; +} +header a:hover { + filter: brightness(1.2); +} +header a::before, +header a::after { + content: ''; + -webkit-mask-image: var(--icon-image); + mask-image: var(--icon-image); + -webkit-mask-size: cover; + mask-size: cover; + width: 1em; + height: 1em; + background-color: var(--header-color); +} +header a.github { + --icon-image: url('/images/github.svg'); +} +header a.github::after { + display: none; +} +header a.sbmt { + --icon-image: url('/images/link.svg'); +} +header a.sbmt::before { + display: none; +} +header a.discord { + --icon-image: url('/images/discord.svg'); +} +header a.discord::before { + width: 1.3em; +} +header a.discord::after { + display: none; +} +.imglink { + font-size: 0.9em; +} +main { + display: grid; + min-height: 0; + margin-top: 2em; + margin-bottom: 3em; +} +footer { + font-weight: 600; + text-align: center; + font-size: 0.8em; + position: sticky; + bottom: 0; + padding: 0.6em 0; + margin-top: auto; +} +footer::before { + content: ''; + pointer-events: none; + -webkit-mask-image: linear-gradient(0deg, black 0%, transparent 100%); + mask-image: linear-gradient(0deg, black 0%, transparent 100%); + position: absolute; + height: 7em; + left: 0; + right: 0; + bottom: 0; + background: var(--background-1); + z-index: 0; +} +footer > * { + z-index: 1; +} +a { + color: var(--text-links); +} +.flexer { + justify-content: flex-start; + align-items: center; + display: flex; + flex-wrap: wrap; +} +.tabs { + justify-content: center; + position: sticky; + top: 2em; + width: calc(100% - 1.5em); + margin: 0 auto; + gap: 1em; + z-index: 1; +} +.tabs .tab-item { + cursor: pointer; + color: var(--text-1); + padding: 0.4em 0.6em; + text-decoration: underline; + text-underline-position: under; + text-decoration-thickness: 1px; + text-decoration-color: rgba(0, 0, 0, 0); + text-underline-offset: 0.3em; + transition: 0.1s ease; + transition-property: color, text-decoration-color; +} +.tabs .tab-item.active { + color: var(--text-links); + text-decoration-color: var(--text-links); +} +.tabs::before { + content: ''; + position: absolute; + top: -2em; + left: -3em; + right: -3em; + bottom: 0; + background: var(--background-1); +} +.indent-left { + margin-left: auto; +} +.results { + display: grid; + gap: 0.5em; +} +.no-results { + text-align: center; + width: 100%; + margin: 2em 0; +} +.result-item { + display: grid; + gap: 0.8em; + color: var(--block-color-1); + padding: 1em; + background: var(--block-1); + border-radius: 0.3em; +} +.result-item hr { + border-color: var(--background-1); +} +.result-item .ri-gallery { + gap: 0.4em; +} +.result-item .ri-gallery img { + height: 7.5em; + border-radius: 0.4em; +} +.result-item .ri-links { + font-size: 0.9em; + gap: 0.4em; + margin-top: 0.4em; +} +.result-item .ri-links img { + height: 1.2em; + border-radius: 0.2em; +} +.result-item .ri-footer { + font-size: 0.9em; + gap: 1em; +} +.copyable { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + cursor: pointer; + padding: 0.1em 0.2em; + background: hsl(var(--block-on-block), 0.2); + transition: 0.5s ease; + transition-property: background, filter; + border-radius: 0.2em; +} +.copyable:active { + filter: brightness(2); + background: hsl(var(--block-on-block), 0.4); + transition: 0.1s ease; +} +.notification { + font-size: 0.8em; + font-weight: bold; + position: absolute; + left: 50%; + bottom: 2em; + padding: 0.5em 1.6em; + translate: 0 0; + box-shadow: 0 0.7em 1.2em rgba(0, 0, 0, 0.7); + transition: 0.3s ease; + transition-property: translate, opacity; + border-radius: 0.3em; +} +.notification.hidden { + opacity: 0; + translate: 0 1em; +} +.notification.red { + color: var(--notification-color-red); + background: var(--notification-block-red); +} +.notification.green { + color: var(--notification-color-green); + background: var(--notification-block-green); +} +@keyframes spin { + 0% { + rotate: 0; + } + 100% { + rotate: 360deg; + } +} +.button { + white-space: nowrap; + text-decoration: none; + font-size: 0.9em; + font-weight: 700; + cursor: pointer; + padding: 0.4em 1em; + border-radius: 0.4em; + transition: 0.1s ease; + transition-property: translate, filter; +} +.button span { + display: block; + pointer-events: none; + transition: 0.3s ease; + transition-property: opacity, width; +} +.button img { + width: 1em; + height: 1em; + -o-object-fit: cover; + object-fit: cover; + transition: 0.3s ease; + transition-property: opacity, width, height; + animation: spin 1s infinite forwards linear; +} +.button:hover { + filter: brightness(1.1); + translate: 0 -0.05em; +} +.button:active { + filter: brightness(0.9); + translate: 0 0.1em; +} +.button.disable { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + pointer-events: none; + filter: grayscale(1); +} +.buttons-group { + font-size: 0.9em; + gap: 10px; +} +.dl-button { + color: var(--background-1); + background: var(--button-download); +} +.delete-button { + color: var(--background-1); + background: var(--button-delete); +} +.open-button { + color: var(--background-1); + background: var(--button-open); +} +.save-button { + color: var(--background-1); + background: var(--button-save); +} +.submit-button { + display: block; + color: var(--background-1); + padding: 0.2em 1em; + background: var(--submit-delete); +} +.search-bar { + font-size: 0.9em; + font-family: inherit; + color: var(--search-color-1); + background: var(--search-block); + padding: 0.2em 0.6em; + border-radius: 0.2em; + outline: 0; + border: 0; + transition: 0.2s ease; + transition-property: filter, opacity; +} +.search-bar::-moz-placeholder { + color: var(--search-color-2); +} +.search-bar::placeholder { + color: var(--search-color-2); +} +.search-bar.disable { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + pointer-events: none; + filter: grayscale(1); + opacity: 0.5; +} +.settings { + display: grid; + gap: 0.8em; + color: var(--block-color-1); + padding: 1em; + background: var(--block-1); + border-radius: 0.3em; +} +.settings .si-btn { + margin-top: 1em; +} +.si div:nth-child(1) { + width: 25em; +} +.si p, +.si .settings-note { + filter: brightness(0.8); + font-size: 0.8em; + max-width: 25em; +} +.si .settings-note { + margin-top: 0.5em; + font-style: italic; +} +.si input[type='text'], +.si input[type='number'] { + font-size: 0.9em; + font-family: inherit; + color: var(--search-color-1); + width: 20em; + padding: 0.2em 0.6em; + background: var(--search-block); + outline: 0; + border-radius: 0.2em; + border: 0; + transition: 0.2s ease; + transition-property: filter, opacity; +} +.si input[type='text']::-moz-placeholder, +.si input[type='number']::-moz-placeholder { + color: var(--search-color-2); +} +.si input[type='text']::placeholder, +.si input[type='number']::placeholder { + color: var(--search-color-2); +} +.si-checkbox { + justify-content: center; + align-items: center; + cursor: pointer; + display: flex; + gap: 0.4em; + height: 1em; +} +.si-checkbox input { + display: none; +} +.si-checkbox input:checked ~ .checkmark::before { + scale: 0.7; + background: var(--search-color-1); +} +.si-checkbox input:checked ~ .status::before { + content: 'Enabled'; +} +.si-checkbox .checkmark { + background: #637a91; + width: 1em; + height: 1em; + border-radius: 0.2em; + overflow: hidden; + display: block; +} +.si-checkbox .checkmark::before { + content: ''; + display: block; + width: 1em; + height: 1em; + scale: 1; + background: #637a91; + transition: background 0.1s ease, scale 0.3s ease; + border-radius: 0.2em; +} +.si-checkbox .status { + font-size: 0.9em; +} +.si-checkbox .status::before { + content: 'Disabled'; +} diff --git a/packages/server/assets/images/discord-5865f2.svg b/packages/server/assets/images/discord-5865f2.svg new file mode 100644 index 00000000..085178cb --- /dev/null +++ b/packages/server/assets/images/discord-5865f2.svg @@ -0,0 +1 @@ +DISCORDDISCORD \ No newline at end of file diff --git a/packages/server/assets/images/discord.svg b/packages/server/assets/images/discord.svg new file mode 100644 index 00000000..7f9a31f0 --- /dev/null +++ b/packages/server/assets/images/discord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/server/assets/images/github-000000.svg b/packages/server/assets/images/github-000000.svg new file mode 100644 index 00000000..2d66f4a1 --- /dev/null +++ b/packages/server/assets/images/github-000000.svg @@ -0,0 +1 @@ +GITHUBGITHUB \ No newline at end of file diff --git a/packages/server/assets/images/github.svg b/packages/server/assets/images/github.svg new file mode 100644 index 00000000..d5e64918 --- /dev/null +++ b/packages/server/assets/images/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/server/assets/images/link.svg b/packages/server/assets/images/link.svg new file mode 100644 index 00000000..9e6a3231 --- /dev/null +++ b/packages/server/assets/images/link.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/server/assets/images/twitter-1DA1F2.svg b/packages/server/assets/images/twitter-1DA1F2.svg new file mode 100644 index 00000000..ed39d439 --- /dev/null +++ b/packages/server/assets/images/twitter-1DA1F2.svg @@ -0,0 +1 @@ +TWITTERTWITTER \ No newline at end of file diff --git a/packages/server/index.ts b/packages/server/index.ts index dbe3c709..ecb382b2 100644 --- a/packages/server/index.ts +++ b/packages/server/index.ts @@ -8,7 +8,7 @@ import { Websocket } from './utils/socket'; export class Server { instanceManager: any; - app = new HttpServer(); + app = new HttpServer(this); constructor({ instanceManager }: { instanceManager: any }) { this.instanceManager = instanceManager; diff --git a/packages/server/router/index.ts b/packages/server/router/index.ts index f3c8a967..e918dd2b 100644 --- a/packages/server/router/index.ts +++ b/packages/server/router/index.ts @@ -1,9 +1,31 @@ import { Beatmap, Calculator } from '@kotrikd/rosu-pp'; -import { config, wLogger } from '@tosu/common'; +import { + config, + downloadFile, + unzip, + wLogger, + writeConfig +} from '@tosu/common'; +import { exec } from 'child_process'; +import fs from 'fs'; import path from 'path'; import { HttpServer, getContentType, sendJson } from '../index'; -import { directoryWalker, readDirectory } from '../utils/directories'; +import { + buildExternalCounters, + buildInstructionLocal, + buildLocalCounters, + buildSettings +} from '../utils/counters'; +import { directoryWalker } from '../utils/directories'; + +const pkgAssetsPath = + 'pkg' in process + ? path.join(__dirname, 'assets') + : path.join(__filename, '../../../assets'); + +const pkgRunningFolder = + 'pkg' in process ? path.dirname(process.execPath) : process.cwd(); export default function buildBaseApi(app: HttpServer) { app.route('/json', 'GET', (req, res) => { @@ -19,17 +41,227 @@ export default function buildBaseApi(app: HttpServer) { sendJson(res, json); }); - app.route('/api/overlays', 'GET', (req, res) => { + app.route(/\/api\/counters\/search\/(?.*)/, 'GET', (req, res) => { + try { + const query = decodeURI(req.params.query) + .replace(/[^a-z0-9A-Z]/, '') + .toLowerCase(); + + if (req.query?.tab == '1') { + return buildExternalCounters(res, query); + } + + return buildLocalCounters(res, query); + } catch (error) { + wLogger.error((error as any).message); + + return sendJson(res, { + error: (error as any).message + }); + } + }); + + app.route(/\/api\/counters\/download\/(?.*)/, 'GET', (req, res) => { + const folderName = req.query.name; + if (!folderName) { + return sendJson(res, { + error: 'no folder name' + }); + } + + const cacheFolder = path.join(pkgRunningFolder, '.cache'); const staticPath = - config.staticFolderPath || - path.join(path.dirname(process.execPath), 'static'); + config.staticFolderPath || path.join(pkgRunningFolder, 'static'); + const folderPath = path.join(staticPath, decodeURI(folderName)); + + const tempPath = path.join(cacheFolder, `${Date.now()}.zip`); + + if (fs.existsSync(folderPath)) { + return sendJson(res, { + error: 'Folder already exist' + }); + } - readDirectory(staticPath, '/', (html: string) => { - res.writeHead(200, { 'Content-Type': getContentType('file.html') }); - res.end(html); + if (!fs.existsSync(cacheFolder)) fs.mkdirSync(cacheFolder); + + try { + const startUnzip = (result) => { + unzip(result, folderPath) + .then(() => { + wLogger.info(`PP Counter downloaded: ${folderName}`); + fs.unlinkSync(tempPath); + + sendJson(res, { + status: 'Finished', + path: result + }); + }) + .catch((reason) => { + fs.unlinkSync(tempPath); + + sendJson(res, { + error: reason + }); + }); + }; + + downloadFile(req.params.url, tempPath) + .then(startUnzip) + .catch((reason) => { + sendJson(res, { + error: reason + }); + }); + } catch (error) { + wLogger.error((error as any).message); + + sendJson(res, { + error: (error as any).message + }); + } + }); + + app.route(/\/api\/counters\/open\/(?.*)/, 'GET', (req, res) => { + try { + const folderName = req.params.name; + if (!folderName) { + return sendJson(res, { + error: 'no folder name' + }); + } + + const staticPath = + config.staticFolderPath || + path.join(pkgRunningFolder, 'static'); + const folderPath = path.join(staticPath, decodeURI(folderName)); + + if (!fs.existsSync(folderPath)) { + return sendJson(res, { + error: `Folder doesn't exists` + }); + } + + // ADDED MULTI PLATFORM SUPPORT + // mac exec(`open "${path}"`, (err, stdout, stderr) => { + // linux exec(`xdg-open "${path}"`, (err, stdout, stderr) => { + + wLogger.info(`PP Counter opened: ${folderName}`); + + exec(`start "" "${folderPath}"`, (err, stdout, stderr) => { + if (err) { + console.error('Error opening file explorer:', err); + return sendJson(res, { + error: `Error opening file explorer: ${err.message}` + }); + } + + return sendJson(res, { + status: 'opened' + }); + }); + } catch (error) { + return sendJson(res, { + error: (error as any).message + }); + } + }); + + app.route(/\/api\/counters\/delete\/(?.*)/, 'GET', (req, res) => { + try { + const folderName = req.params.name; + if (!folderName) { + return sendJson(res, { + error: 'no folder name' + }); + } + + const staticPath = + config.staticFolderPath || + path.join(pkgRunningFolder, 'static'); + const folderPath = path.join(staticPath, decodeURI(folderName)); + + if (!fs.existsSync(folderPath)) { + return sendJson(res, { + error: `Folder doesn't exists` + }); + } + + wLogger.info(`PP Counter removed: ${folderName}`); + + fs.rmSync(folderPath, { recursive: true, force: true }); + return sendJson(res, { + status: 'deleted' + }); + } catch (error) { + return sendJson(res, { + error: (error as any).message + }); + } + }); + + app.route('/api/settingsSave', 'POST', (req, res) => { + const body = req.body; + // try { + // body = JSON.parse(req.body); + // } catch (error) { + // return sendJson(res, { + // error: (error as any).message, + // }); + // }; + + if (body == '') { + return sendJson(res, { + error: 'No settings' + }); + } + + writeConfig(app.Server, body); + + sendJson(res, { + status: 'updated' }); }); + app.route(/\/images\/(?.*)/, 'GET', (req, res) => { + fs.readFile( + path.join(pkgAssetsPath, 'images', req.params.filePath), + (err, content) => { + res.writeHead(200, { + 'Content-Type': getContentType(req.params.filePath) + }); + + res.end(content); + } + ); + }); + + app.route('/homepage.min.css', 'GET', (req, res) => { + // FIXME: REMOVE THAT SHIT + fs.readFile( + path.join(pkgAssetsPath, 'homepage.min.css'), + 'utf8', + (err, content) => { + res.writeHead(200, { + 'Content-Type': getContentType('homepage.min.css') + }); + res.end(content); + } + ); + }); + + app.route('/homepage.js', 'GET', (req, res) => { + fs.readFile( + path.join(pkgAssetsPath, 'homepage.js'), + 'utf8', + (err, content) => { + res.writeHead(200, { + 'Content-Type': getContentType('homepage.js') + }); + res.end(content); + } + ); + }); + app.route('/api/calculate/pp', 'GET', (req, res) => { try { const query: any = req.query; @@ -81,33 +313,48 @@ export default function buildBaseApi(app: HttpServer) { }); app.route(/.*/, 'GET', (req, res) => { - const url = req.pathname || '/'; - const folderPath = - config.staticFolderPath || - path.join(path.dirname(process.execPath), 'static'); + try { + const url = req.pathname || '/'; + const folderPath = + config.staticFolderPath || + path.join(pkgRunningFolder, 'static'); - if (url == '/') { - return readDirectory(folderPath, url, (html: string) => { - res.writeHead(200, { - 'Content-Type': getContentType('file.html') - }); - res.end(html); - }); - } + if (url == '/') { + if (req.query?.tab == '1') { + return buildExternalCounters(res); + } - const extension = path.extname(url); - if (extension == '' && !url.endsWith('/')) { - res.writeHead(301, { Location: url + '/' }); - return res.end(); - } + if (req.query?.tab == '2') { + return buildSettings(res); + } - const selectIndexHTML = url.endsWith('/') ? url + 'index.html' : url; - directoryWalker({ - _htmlRedirect: true, - res, - baseUrl: url, - pathname: selectIndexHTML, - folderPath - }); + if (req.query?.tab == '3') { + return buildInstructionLocal(res); + } + + return buildLocalCounters(res); + } + + const extension = path.extname(url); + if (extension == '' && !url.endsWith('/')) { + res.writeHead(301, { Location: url + '/' }); + return res.end(); + } + + const selectIndexHTML = url.endsWith('/') + ? url + 'index.html' + : url; + directoryWalker({ + _htmlRedirect: true, + res, + baseUrl: url, + pathname: selectIndexHTML, + folderPath + }); + } catch (error) { + return sendJson(res, { + error: (error as any).message + }); + } }); } diff --git a/packages/server/utils/counters.ts b/packages/server/utils/counters.ts new file mode 100644 index 00000000..89220a7b --- /dev/null +++ b/packages/server/utils/counters.ts @@ -0,0 +1,513 @@ +import { config, recursiveFilesSearch } from '@tosu/common'; +import fs from 'fs'; +import http from 'http'; +import path from 'path'; + +import { getContentType } from '../utils'; +import { + authorHTML, + authorLinksHTML, + checkboxHTML, + emptyCounters, + emptyNotice, + galleryImageHTML, + icons_images, + iframeHTML, + inputHTML, + metadataHTML, + nameHTML, + noMoreCounters, + resultItemHTML, + saveSettingsButtonHTML, + settingsItemHTML +} from './htmls'; + +/** + * ТАК КАК БЛЯТЬ У НАС В ЖЫЭСЕ + * НЕ ПРИДУМАЛИ НОРМАЛЬНО ПАКЕТИРОВАТЬ ГОВНО БЕЗ ВЕБПАКА + * ИДЕМ И ПИЛИМ КОСТЫЛИ + * kys js! + */ +const pkgAssetsPath = + 'pkg' in process + ? path.join(__dirname, 'assets') + : path.join(__filename, '../../../assets'); + +const pkgRunningFolder = + 'pkg' in process ? path.dirname(process.execPath) : process.cwd(); + +function splitTextByIndex(text, letter) { + const index = text.indexOf(letter); + if (index === -1) { + return [text]; + } else { + const part1 = text.substring(0, index); + const part2 = text.substring(index); + + return [part1, part2]; + } +} + +export function parseTXT(filePath: string) { + const content = fs.readFileSync(filePath, 'utf8')?.split('\n'); + + const object: any = {}; + for (let i = 0; i < content.length; i++) { + const line = content[i]; + const result = splitTextByIndex(line, ':'); + let [key, value] = result; + if (key == null || value == null) continue; + value = value.split('##')[0].replace(/\r/, '').replace(':', ''); + + if (/[0-9-]+x[0-9-]+/.test(value)) + object[key.toLowerCase()] = value.split('x'); + else object[key.toLowerCase()] = value.trim(); + } + + if (object.resolution) + object.resolution = object.resolution.map((r) => r.trim()); + if (object.authorlinks) object.authorlinks = object.authorlinks.split(','); + + delete object.compatiblewith; + delete object.usecase; + + return object; +} + +function rebuildJSON({ + array, + external, + query +}: { + array: { + name: string; + author: string; + resolution: number[]; + authorlinks: string[]; + + usecase?: string; + compatiblewith?: string; + assets?: { + type: string; + url: string; + }[]; + downloadLink?: string; + }[]; + external?: boolean; + query?: string; +}) { + let items = ''; + for (let i = 0; i < array.length; i++) { + const item = array[i]; + + if (query != null) { + if ( + !( + item.name.toLowerCase().includes(query) || + item.name.toLowerCase().includes(query) + ) + ) + continue; + } + + const name = nameHTML.replace('{NAME}', item.name); + const author = authorHTML.replace('{AUTHOR}', item.author); + + const counterName = + (item.author || '').toLowerCase() == 'local' + ? item.name + : `${item.name} by ${item.author}`; + + const links = item.authorlinks + .map((r) => { + const domain = + /:\/\/(?\S+)\//.exec(r)?.groups?.domain || ''; + if (!domain) return null; + + const icon_url = icons_images[domain.toLowerCase()]; + if (!icon_url) return null; + + return authorLinksHTML + .replace('{LINK}', r) + .replace('{ICON_URL}', icon_url); + }) + .filter((r) => r != null) + .join(' '); + + const iframe = iframeHTML + .replace( + '{URL}', + `http://${config.serverIP}:${config.serverPort}/${counterName}/` + ) + .replace( + '{WIDTH}', + item.resolution[0] == -1 + ? '500px' + : item.resolution[0] == -2 + ? '100%' + : `${item.resolution[0]}px` + ) + .replace( + '{HEIGHT}', + item.resolution[1] == -1 ? '500px' : `${item.resolution[1]}px` + ); + + const metadata = metadataHTML + .replace( + '{COPY_URL}', + `http://${config.serverIP}:${config.serverPort}/${counterName}/` + ) + .replace('{TEXT_URL}', `/${counterName}/`) + .replace( + '{COPY_X}', + item.resolution[0] == -1 || item.resolution[0] == -2 + ? 'ANY' + : item.resolution[0].toString() + ) + .replace( + '{X}', + item.resolution[0] == -1 || item.resolution[0] == -2 + ? 'ANY' + : item.resolution[0].toString() + ) + .replace( + '{COPY_Y}', + item.resolution[1] == -1 || item.resolution[1] == -2 + ? 'ANY' + : item.resolution[1].toString() + ) + .replace( + '{Y}', + item.resolution[1] == -1 || item.resolution[1] == -2 + ? 'ANY' + : item.resolution[1].toString() + ); + + const button = item.downloadLink + ? `
` + : `
+ + +
`; + + const assets = (item.assets || []) + .map((r) => { + return galleryImageHTML.replace('{LINK}', r.url); + }) + .filter((r) => r != null) + .join(' '); + + const gallery = item.assets ? assets : iframe; + + const footer = + external != true + ? `` + : ''; + + items += resultItemHTML + .replace('{NAME}', name) + .replace('{AUTHOR}', author) + .replace('{AUTHOR_LINKS}', links) + .replace('{BUTTONS}', button) + .replace('{GALLERY}', gallery) + .replace('{FOOTER}', footer); + } + + return items; +} + +function getLocalCounters() { + const staticPath = + config.staticFolderPath || path.join(pkgRunningFolder, 'static'); + + const countersListTXT = recursiveFilesSearch({ + dir: staticPath, + fileList: [], + filename: 'metadata.txt' + }); + + const countersListHTML = recursiveFilesSearch({ + dir: staticPath, + fileList: [], + filename: 'index.html' + }); + + const arrayOfLocal = countersListHTML + .filter((r) => { + const folder = path.dirname(r); + return ( + countersListTXT.find((s) => path.dirname(s) == folder) == null + ); + }) + .map((r) => ({ + name: path.basename(path.dirname(r)), + author: 'local', + resolution: [-2, '400'], + authorlinks: [] + })); + + const array = countersListTXT.map((r) => parseTXT(r)); + return array.concat(arrayOfLocal).filter((r) => r.name != ''); +} + +export function buildLocalCounters(res: http.ServerResponse, query?: string) { + const array = getLocalCounters(); + const build = rebuildJSON({ + array, + external: false, + query + }); + + if (query != null) { + res.writeHead(200, { + 'Content-Type': getContentType('file.html') + }); + return res.end(build || emptyCounters); + } + + fs.readFile( + path.join(pkgAssetsPath, 'homepage.html'), + 'utf8', + (err, content) => { + const html = content.replace('{{LIST}}', build || emptyNotice); + + res.writeHead(200, { + 'Content-Type': getContentType('file.html') + }); + res.end(html); + } + ); +} + +export async function buildExternalCounters( + res: http.ServerResponse, + query?: string +) { + const request = await fetch( + `https://raw.githubusercontent.com/cyperdark/osu-counters/master/.github/api.json` + ); + const json: any = await request.json(); + + const exists = getLocalCounters(); + const array = json.filter( + (r) => !exists.find((s) => s.name == r.name && s.author == r.author) + ); + + const build = rebuildJSON({ + array, + external: true, + query + }); + + if (query != null) { + res.writeHead(200, { + 'Content-Type': getContentType('file.html') + }); + return res.end(build || emptyCounters); + } + + fs.readFile( + path.join(pkgAssetsPath, 'homepage.html'), + 'utf8', + (err, content) => { + const html = content.replace('{{LIST}}', build || noMoreCounters); + + res.writeHead(200, { + 'Content-Type': getContentType('file.html') + }); + res.end(html); + } + ); +} + +export function buildSettings(res: http.ServerResponse) { + const debugHTML = settingsItemHTML + .replace('{NAME}', `DEBUG_LOG`) + .replace( + '{DESCRIPTION}', + `Enables logs for tosu developers, not very intuitive for you, the end user.
best not to include without developer's request.` + ) + .replace( + '{INPUT}', + checkboxHTML + .replace(/{NAME}/gm, 'DEBUG_LOG') + .replace('{ADDON}', config.debugLogging ? 'checked="true"' : '') + .replace('{VALUE}', `${config.debugLogging}`) + ); + + const calculatePPHTML = settingsItemHTML + .replace('{NAME}', `CALCULATE_PP`) + .replace( + '{DESCRIPTION}', + `Turns PP counting on/off. Very useful for tournament client, you only care about scoring and map stats for example` + ) + .replace( + '{INPUT}', + checkboxHTML + .replace(/{NAME}/gm, 'CALCULATE_PP') + .replace('{ADDON}', config.calculatePP ? 'checked="true"' : '') + .replace('{VALUE}', `${config.calculatePP}`) + ); + + const enableKeyOverlayHTML = settingsItemHTML + .replace('{NAME}', `ENABLE_KEY_OVERLAY`) + .replace( + '{DESCRIPTION}', + `Enables/disables reading K1/K2/M1/M2 keys on the keyboard` + ) + .replace( + '{INPUT}', + checkboxHTML + .replace(/{NAME}/gm, 'ENABLE_KEY_OVERLAY') + .replace( + '{ADDON}', + config.enableKeyOverlay ? 'checked="true"' : '' + ) + .replace('{VALUE}', `${config.enableKeyOverlay}`) + ); + + const enableGosuOverlayHTML = settingsItemHTML + .replace('{NAME}', `ENABLE_GOSU_OVERLAY`) + .replace( + '{DESCRIPTION}', + `Enables/disables the in-game gosumemory overlay
(!!!I AM NOT RESPONSIBLE FOR USING IT!!!)` + ) + .replace( + '{INPUT}', + checkboxHTML + .replace(/{NAME}/gm, 'ENABLE_GOSU_OVERLAY') + .replace( + '{ADDON}', + config.enableGosuOverlay ? 'checked="true"' : '' + ) + .replace('{VALUE}', `${config.enableGosuOverlay}`) + ); + + const pollRateHTML = settingsItemHTML + .replace('{NAME}', `POLL_RATE`) + .replace( + '{DESCRIPTION}', + `Once in what value, the programme should read the game values (in milliseconds)` + ) + .replace( + '{INPUT}', + inputHTML + .replace('{TYPE}', 'number') + .replace(/{NAME}/gm, 'POLL_RATE') + .replace('{ADDON}', config.pollRate ? 'min="0"' : '') + .replace('{VALUE}', `${config.pollRate}`) + ); + + const keyOverlayPollRateHTML = settingsItemHTML + .replace('{NAME}', `KEYOVERLAY_POLL_RATE`) + .replace( + '{DESCRIPTION}', + `Once per value, the programme should read the values of keys K1/K2/M1/M2 (in milliseconds)` + ) + .replace( + '{INPUT}', + inputHTML + .replace('{TYPE}', 'number') + .replace(/{NAME}/gm, 'KEYOVERLAY_POLL_RATE') + .replace('{ADDON}', config.keyOverlayPollRate ? 'min="0"' : '') + .replace('{VALUE}', `${config.keyOverlayPollRate}`) + ); + + const serverIPHTML = settingsItemHTML + .replace('{NAME}', `SERVER_IP`) + .replace( + '{DESCRIPTION}', + `IP address where the websocket api server will be registered` + ) + .replace( + '{INPUT}', + inputHTML + .replace('{TYPE}', 'text') + .replace(/{NAME}/gm, 'SERVER_IP') + .replace('{ADDON}', config.serverIP ? 'min="0"' : '') + .replace('{VALUE}', `${config.serverIP}`) + ); + + const serverPortHTML = settingsItemHTML + .replace('{NAME}', `SERVER_PORT`) + .replace( + '{DESCRIPTION}', + `The port on which the websocket api server will run` + ) + .replace( + '{INPUT}', + inputHTML + .replace('{TYPE}', 'number') + .replace(/{NAME}/gm, 'SERVER_PORT') + .replace('{ADDON}', config.serverPort ? 'min="0"' : '') + .replace('{VALUE}', `${config.serverPort}`) + ); + + const staticFolderPathtHTML = settingsItemHTML + .replace('{NAME}', `STATIC_FOLDER_PATH`) + .replace( + '{DESCRIPTION}', + `The folder from which the overlays will be taken.` + ) + .replace( + '{INPUT}', + inputHTML + .replace('{TYPE}', 'text') + .replace(/{NAME}/gm, 'STATIC_FOLDER_PATH') + .replace('{ADDON}', config.staticFolderPath ? 'min="0"' : '') + .replace('{VALUE}', `${config.staticFolderPath}`) + ); + + const settings = `
+ ${debugHTML} + ${calculatePPHTML} + ${enableKeyOverlayHTML} + ${enableGosuOverlayHTML} + ${pollRateHTML} + ${keyOverlayPollRateHTML} + ${serverIPHTML} + ${serverPortHTML} + ${staticFolderPathtHTML} + ${saveSettingsButtonHTML} +
`; + + fs.readFile( + path.join(pkgAssetsPath, 'homepage.html'), + // '../assets/homepage.html', + 'utf8', + (err, content) => { + const html = content.replace('{{LIST}}', settings); + + res.writeHead(200, { + 'Content-Type': getContentType('file.html') + }); + res.end(html); + } + ); +} + +export function buildInstructionLocal(res: http.ServerResponse) { + const pageContent = `
+

How to Add Your Own Counter Locally

+

+ 1. Create a new folder:
- First, create a new folder inside your static folder.

+ 2. Move your pp counter files:
- Next, move your pp counter files into the newly created folder.

+ 3. Download and place metadata file:
- Download the metadata.txt file and place it in the counter folder.

+ 4. Fill out the metadata file:
- Finally, open the metadata.txt file and fill out the necessary information. +

+
`; + fs.readFile( + path.join(pkgAssetsPath, 'homepage.html'), + 'utf8', + (err, content) => { + const html = content.replace('{{LIST}}', pageContent); + + res.writeHead(200, { + 'Content-Type': getContentType('file.html') + }); + res.end(html); + } + ); +} diff --git a/packages/server/utils/htmls.ts b/packages/server/utils/htmls.ts new file mode 100644 index 00000000..874b75e1 --- /dev/null +++ b/packages/server/utils/htmls.ts @@ -0,0 +1,57 @@ +export const icons_images = { + 'github.com': '/images/github-000000.svg', + 'twitter.com': '/images/twitter-1DA1F2.svg', + 'discord.gg': '/images/discord-5865f2.svg', + 'discord.com': '/images/discord-5865f2.svg' +}; + +export const emptyNotice = ``; +export const emptyCounters = `
No counters
Change your search phrase
`; +export const noMoreCounters = `
Nice job!
You downloaded all available pp counters
`; + +export const iframeHTML = ``; + +export const metadataHTML = ` +
URL: {TEXT_URL}
+
Resolution: {X} x {Y}
+`; + +export const nameHTML = `

{NAME}

`; +export const authorHTML = `by {AUTHOR}`; +export const authorLinksHTML = ``; + +export const galleryImageHTML = ``; + +export const resultItemHTML = ` +
+
+
+ {NAME} + +
+ {BUTTONS} +
+
+ + {FOOTER} +
`; + +export const settingsItemHTML = ` +
+
+

{NAME}

+

{DESCRIPTION}

+
+ {INPUT} +
`; + +export const checkboxHTML = ` +`; + +export const inputHTML = ``; + +export const saveSettingsButtonHTML = `
`; diff --git a/packages/server/utils/http.ts b/packages/server/utils/http.ts index 7208ba71..1382ca02 100644 --- a/packages/server/utils/http.ts +++ b/packages/server/utils/http.ts @@ -1,8 +1,11 @@ import { wLogger } from '@tosu/common'; import http, { IncomingMessage, ServerResponse } from 'http'; +import { Server } from '../index'; + export interface ExtendedIncomingMessage extends IncomingMessage { instanceManager: any; + body: string; query: { [key: string]: string }; params: { [key: string]: string }; pathname: string; @@ -26,6 +29,7 @@ type RouteHandler = { export class HttpServer { private middlewares: RequestHandler[] = []; server: http.Server; + Server: Server; private routes: { [method: string]: { path: string | RegExp; @@ -33,7 +37,8 @@ export class HttpServer { }[]; } = {}; - constructor() { + constructor(hold: any) { + this.Server = hold; // @ts-ignore this.server = http.createServer(this.handleRequest.bind(this)); } @@ -68,6 +73,7 @@ export class HttpServer { ) { const startTime = performance.now(); let index = 0; + let body = ''; res.on('finish', () => { const finishTime = performance.now(); @@ -86,6 +92,19 @@ export class HttpServer { return; } + // get data aka body + if (['POST', 'PUT', 'PATCH'].includes(req.method || '')) { + req.on('data', (chunk) => { + body += chunk; + }); + + req.on('end', () => { + req.body = body; + + this.handleNext(req, res); + }); + return; + } this.handleNext(req, res); }; diff --git a/packages/server/utils/index.ts b/packages/server/utils/index.ts index 1404bbda..c1ca62cd 100644 --- a/packages/server/utils/index.ts +++ b/packages/server/utils/index.ts @@ -2,70 +2,70 @@ import http from 'http'; import path from 'path'; const contentTypes = { - '.html': 'text/html', - '.js': 'text/javascript', - '.css': 'text/css', - '.json': 'application/json', - '.png': 'image/png', - '.jpg': 'image/jpg', - '.gif': 'image/gif', - '.svg': 'image/svg+xml', - '.wav': 'audio/wav', - '.mp4': 'video/mp4', - '.woff': 'application/font-woff', - '.ttf': 'application/font-ttf', - '.eot': 'application/vnd.ms-fontobject', - '.otf': 'application/font-otf', - '.wasm': 'application/wasm', - '.aac': 'audio/aac', - '.abw': 'application/x-abiword', - '.apng': 'image/apng', - '.arc': 'application/x-freearc', - '.avif': 'image/avif', - '.avi': 'video/x-msvideo', - '.bin': 'application/octet-stream', - '.bmp': 'image/bmp', - '.bz': 'application/x-bzip', - '.bz2': 'application/x-bzip2', - '.cda': 'application/x-cdf', - '.csv': 'text/csv', - '.gz': 'application/gzip', - '.htm': 'text/html', - '.ico': 'image/vnd.microsoft.icon', - '.jpeg': 'image/jpeg', - '.jsonld': 'application/ld+json', - '.mid': 'audio/x-midi', - '.mjs': 'text/javascript', - '.mp3': 'audio/mpeg', - '.mpeg': 'video/mpeg', - '.mpkg': 'application/vnd.apple.installer+xml', - '.ogg': 'audio/ogg', - '.oga': 'audio/ogg', - '.ogv': 'video/ogg', - '.ogx': 'application/ogg', - '.opus': 'audio/opus', - '.rar': 'application/vnd.rar', - '.sh': 'application/x-sh', - '.tar': 'application/x-tar', - '.tif': 'image/tiff', - '.ts': 'video/mp2t', - '.txt': 'text/plain', - '.weba': 'audio/webm', - '.webm': 'video/webm', - '.webp': 'image/webp', - '.woff2': 'font/woff2', - '.xhtml': 'application/xhtml+xml', - '.xml': 'application/xmlz', - '.xul': 'application/vnd.mozilla.xul+xml', - '.zip': 'application/zip', - '.3gp': 'video/3gpp;', - '.3g2': 'video/3gpp2;', - '.7z': 'application/x-7z-compressed', - '.osu': 'text/plain', + '.html': 'text/html; charset=utf-8', + '.js': 'text/javascript; charset=utf-8', + '.css': 'text/css; charset=utf-8', + '.json': 'application/json; charset=utf-8', + '.png': 'image/png; charset=utf-8', + '.jpg': 'image/jpg; charset=utf-8', + '.gif': 'image/gif; charset=utf-8', + '.svg': 'image/svg+xml; charset=utf-8', + '.wav': 'audio/wav; charset=utf-8', + '.mp4': 'video/mp4; charset=utf-8', + '.woff': 'application/font-woff; charset=utf-8', + '.ttf': 'application/font-ttf; charset=utf-8', + '.eot': 'application/vnd.ms-fontobject; charset=utf-8', + '.otf': 'application/font-otf; charset=utf-8', + '.wasm': 'application/wasm; charset=utf-8', + '.aac': 'audio/aac; charset=utf-8', + '.abw': 'application/x-abiword; charset=utf-8', + '.apng': 'image/apng; charset=utf-8', + '.arc': 'application/x-freearc; charset=utf-8', + '.avif': 'image/avif; charset=utf-8', + '.avi': 'video/x-msvideo; charset=utf-8', + '.bin': 'application/octet-stream; charset=utf-8', + '.bmp': 'image/bmp; charset=utf-8', + '.bz': 'application/x-bzip; charset=utf-8', + '.bz2': 'application/x-bzip2; charset=utf-8', + '.cda': 'application/x-cdf; charset=utf-8', + '.csv': 'text/csv; charset=utf-8', + '.gz': 'application/gzip; charset=utf-8', + '.htm': 'text/html; charset=utf-8', + '.ico': 'image/vnd.microsoft.icon; charset=utf-8', + '.jpeg': 'image/jpeg; charset=utf-8', + '.jsonld': 'application/ld+json; charset=utf-8', + '.mid': 'audio/x-midi; charset=utf-8', + '.mjs': 'text/javascript; charset=utf-8', + '.mp3': 'audio/mpeg; charset=utf-8', + '.mpeg': 'video/mpeg; charset=utf-8', + '.mpkg': 'application/vnd.apple.installer+xml; charset=utf-8', + '.ogg': 'audio/ogg; charset=utf-8', + '.oga': 'audio/ogg; charset=utf-8', + '.ogv': 'video/ogg; charset=utf-8', + '.ogx': 'application/ogg; charset=utf-8', + '.opus': 'audio/opus; charset=utf-8', + '.rar': 'application/vnd.rar; charset=utf-8', + '.sh': 'application/x-sh; charset=utf-8', + '.tar': 'application/x-tar; charset=utf-8', + '.tif': 'image/tiff; charset=utf-8', + '.ts': 'video/mp2t; charset=utf-8', + '.txt': 'text/plain; charset=utf-8', + '.weba': 'audio/webm; charset=utf-8', + '.webm': 'video/webm; charset=utf-8', + '.webp': 'image/webp; charset=utf-8', + '.woff2': 'font/woff2; charset=utf-8', + '.xhtml': 'application/xhtml+xml; charset=utf-8', + '.xml': 'application/xmlz; charset=utf-8', + '.xul': 'application/vnd.mozilla.xul+xml; charset=utf-8', + '.zip': 'application/zip; charset=utf-8', + '.3gp': 'video/3gpp;; charset=utf-8', + '.3g2': 'video/3gpp2;; charset=utf-8', + '.7z': 'application/x-7z-compressed; charset=utf-8', + '.osu': 'text/plain; charset=utf-8', - '.midi': 'audio/x-midi', - '.swf': 'application/x-shockwave-flash', - '.tiff': 'image/tiff' + '.midi': 'audio/x-midi; charset=utf-8', + '.swf': 'application/x-shockwave-flash; charset=utf-8', + '.tiff': 'image/tiff; charset=utf-8' }; export function getContentType(text: string) { diff --git a/packages/server/utils/socket.ts b/packages/server/utils/socket.ts index be0399f3..8142bbfe 100644 --- a/packages/server/utils/socket.ts +++ b/packages/server/utils/socket.ts @@ -42,7 +42,7 @@ export class Websocket { wLogger.debug(`[${ws.id}](${this.clients.size}) >>> ws: CONNECTED`); - this.socket.on('close', (reason, description) => { + ws.on('close', (reason, description) => { this.clients.delete(ws.id); wLogger.debug( @@ -50,7 +50,7 @@ export class Websocket { ); }); - this.socket.on('error', (reason, description) => { + ws.on('error', (reason, description) => { this.clients.delete(ws.id); wLogger.debug( diff --git a/packages/tosu/package.json b/packages/tosu/package.json index 5d142686..73b7b0ea 100644 --- a/packages/tosu/package.json +++ b/packages/tosu/package.json @@ -8,15 +8,16 @@ "ts:run": "cross-env NODE_ENV=development ts-node --transpile-only -r tsconfig-paths/register --project tsconfig.json", "ts:compile": "ncc build src/index.ts -o dist -m -d", "run:dev": "pnpm run genver && pnpm run ts:run src/index.ts", - "compile": "pnpm run genver && pnpm run ts:compile && pkg -t node18-win-x64 --output dist/tosu.exe --debug --config pkg.json --compress brotli dist/index.js && pnpm run ts:run src/postBuild.ts" + "compile:prepare-htmls": "cp -rf node_modules/@tosu/server/assets ./dist", + "compile": "pnpm run genver && pnpm run ts:compile && pnpm run compile:prepare-htmls && pkg -t node18-win-x64 --output dist/tosu.exe --debug --config pkg.json --compress brotli dist/index.js && pnpm run ts:run src/postBuild.ts" }, "dependencies": { "@kotrikd/rosu-pp": "^0.10.0", "@tosu/common": "workspace:*", "@tosu/find-process": "workspace:*", + "@tosu/game-overlay": "workspace:*", "@tosu/server": "workspace:*", "@tosu/updater": "workspace:*", - "@tosu/game-overlay": "workspace:*", "osu-catch-stable": "^4.0.0", "osu-classes": "^3.0.0", "osu-mania-stable": "^5.0.0", diff --git a/packages/tosu/pkg.json b/packages/tosu/pkg.json index 76ae8d39..ea043320 100644 --- a/packages/tosu/pkg.json +++ b/packages/tosu/pkg.json @@ -3,10 +3,11 @@ "assets": [ "dist/**/*.node", "dist/target/**/*", - "dist/_version.js" + "dist/_version.js", + "dist/assets/**/*" ], "targets": [ "node18-win-x64" ], "outputPath": "dist" -} \ No newline at end of file +} \ No newline at end of file diff --git a/packages/updater/index.ts b/packages/updater/index.ts index 7c08d8c5..24fe19eb 100644 --- a/packages/updater/index.ts +++ b/packages/updater/index.ts @@ -1,8 +1,13 @@ -import { downloadFile, platformResolver, sleep, wLogger } from '@tosu/common'; +import { + downloadFile, + platformResolver, + sleep, + unzipTosu, + wLogger +} from '@tosu/common'; import { exec, spawn } from 'child_process'; import fs from 'fs'; import path from 'path'; -import unzipper from 'unzipper'; // NOTE: _version.js packs with pkg support in tosu build const currentVersion = require(process.cwd() + '/_version.js'); @@ -26,30 +31,6 @@ const deleteNotLocked = async (filePath: string) => { } }; -const unzipAsset = (zipPath: string, extractPath: string): Promise => - new Promise((resolve, reject) => { - const zip = fs.createReadStream(zipPath).pipe(unzipper.Parse()); - - zip.on('entry', async (entry) => { - const fileName = entry.path; - if (fileName === 'tosu' || fileName === 'tosu.exe') { - const { name, ext } = path.parse(fileName); - const modifyName = path.join(extractPath, `${name}_new${ext}`); - - entry - .pipe(fs.createWriteStream(modifyName)) - .on('finish', () => { - resolve(modifyName); - }); - } else { - entry.autodrain(); - } - }); - - zip.on('error', reject); - // zip.on('finish', resolve); - }); - export const autoUpdater = () => new Promise(async (resolve) => { wLogger.info('Checking updates'); @@ -105,7 +86,7 @@ export const autoUpdater = () => fileDestination ); - const unzipExecutable = await unzipAsset(downloadAsset, process.cwd()); + const unzipExecutable = await unzipTosu(downloadAsset, process.cwd()); const currentExecutablePath = process.argv[0]; // Path to the current executable @@ -122,7 +103,6 @@ export const autoUpdater = () => oldProcess.unref(); - let started = false; exec( `start "" "${newExecutablePath}"`, async (error, stdout, stderr) => { @@ -131,18 +111,14 @@ export const autoUpdater = () => return; } - await sleep(1000); - started = true; - } - ); + await sleep(2500); - while (started == false) { - await sleep(1000); - } + wLogger.info('Closing program'); - wLogger.info('Closing program'); + await sleep(1000); - await sleep(1000); - oldProcess.kill(); - process.exit(); + oldProcess.kill(); + process.exit(); + } + ); }); diff --git a/packages/updater/package.json b/packages/updater/package.json index 554e32ed..50a4201b 100644 --- a/packages/updater/package.json +++ b/packages/updater/package.json @@ -10,7 +10,6 @@ "build": "tsc" }, "dependencies": { - "unzipper": "^0.10.14", "@tosu/common": "workspace:*" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d33c703c..d1ac5e42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,6 +36,9 @@ importers: packages/common: dependencies: + adm-zip: + specifier: ^0.5.10 + version: 0.5.10 dotenv: specifier: ^16.0.3 version: 16.3.1 @@ -155,9 +158,6 @@ importers: '@tosu/common': specifier: workspace:* version: link:../common - unzipper: - specifier: ^0.10.14 - version: 0.10.14 packages: @@ -530,6 +530,11 @@ packages: resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} dev: false + /adm-zip@0.5.10: + resolution: {integrity: sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==} + engines: {node: '>=6.0'} + dev: false + /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -621,18 +626,6 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - /big-integer@1.6.52: - resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} - engines: {node: '>=0.6'} - dev: false - - /binary@0.3.0: - resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} - dependencies: - buffers: 0.1.1 - chainsaw: 0.1.0 - dev: false - /bl@1.2.3: resolution: {integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==} dependencies: @@ -648,10 +641,6 @@ packages: readable-stream: 3.6.2 dev: true - /bluebird@3.4.7: - resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} - dev: false - /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -695,22 +684,12 @@ packages: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: false - /buffer-indexof-polyfill@1.0.2: - resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} - engines: {node: '>=0.10'} - dev: false - /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - /buffers@0.1.1: - resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} - engines: {node: '>=0.2.0'} - dev: false - /cacache@18.0.2: resolution: {integrity: sha512-r3NU8h/P+4lVUHfeRw1dtgQYar3DZMm4/cm2bZgOvrFC/su7budSOeqh52VJIC4U4iG1WWwV6vRW0znqBvxNuw==} engines: {node: ^16.14.0 || >=18.0.0} @@ -747,12 +726,6 @@ packages: resolution: {integrity: sha512-3j4DaoTrsCD1MRkTF2Soacii0Nx7UHCce0EwUf4fHnggwiE4fbmF2AbnfzayR36DF8KGadfh7M/Yfy625kgPlA==} hasBin: true - /chainsaw@0.1.0: - resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} - dependencies: - traverse: 0.3.9 - dev: false - /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1220,12 +1193,6 @@ packages: minimatch: 3.1.2 dev: false - /duplexer2@0.1.4: - resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} - dependencies: - readable-stream: 2.3.8 - dev: false - /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1441,20 +1408,6 @@ packages: minipass: 7.0.4 dev: false - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: false - - /fstream@1.0.12: - resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} - engines: {node: '>=0.6'} - dependencies: - graceful-fs: 4.2.11 - inherits: 2.0.4 - mkdirp: 0.5.6 - rimraf: 2.7.1 - dev: false - /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -1553,17 +1506,6 @@ packages: path-scurry: 1.10.1 dev: false - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: false - /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -1705,13 +1647,6 @@ packages: engines: {node: '>=8'} dev: false - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - dev: false - /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -1917,10 +1852,6 @@ packages: - supports-color dev: true - /listenercount@1.0.1: - resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} - dev: false - /listr2@6.6.1: resolution: {integrity: sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==} engines: {node: '>=16.0.0'} @@ -2210,13 +2141,6 @@ packages: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} dev: true - /mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - dependencies: - minimist: 1.2.8 - dev: false - /mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} @@ -2511,11 +2435,6 @@ packages: engines: {node: '>=8'} dev: false - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: false - /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2821,13 +2740,6 @@ packages: resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} dev: true - /rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true - dependencies: - glob: 7.2.3 - dev: false - /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -2875,10 +2787,6 @@ packages: dependencies: lru-cache: 6.0.0 - /setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - dev: false - /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -3219,10 +3127,6 @@ packages: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: true - /traverse@0.3.9: - resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} - dev: false - /trim-newlines@3.0.1: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} @@ -3345,21 +3249,6 @@ packages: engines: {node: '>= 10.0.0'} dev: true - /unzipper@0.10.14: - resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} - dependencies: - big-integer: 1.6.52 - binary: 0.3.0 - bluebird: 3.4.7 - buffer-indexof-polyfill: 1.0.2 - duplexer2: 0.1.4 - fstream: 1.0.12 - graceful-fs: 4.2.11 - listenercount: 1.0.1 - readable-stream: 2.3.8 - setimmediate: 1.0.5 - dev: false - /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}