From 09b5e9c0815f2e9861cacbfd3a15fb79a60cf39b Mon Sep 17 00:00:00 2001 From: TheArcaneBrony Date: Fri, 26 Aug 2022 04:14:54 +0200 Subject: [PATCH] Basic client patching system --- .gitignore | 2 + assets/fosscord-login.css | 68 ----------------- assets/fosscord.css | 94 +---------------------- assets/index.html | 8 +- assets/private/icons/custom/.gitkeep | 0 assets/private/icons/homeIcon.path | 1 + fosscord-server.code-workspace | 8 ++ scripts/patches/applyPatches.js | 24 ++++++ scripts/patches/mkPatches.js | 40 ++++++++++ scripts/patches/prepWS.js | 75 +++++++++++++++++++ scripts/patches/resetWS.js | 24 ++++++ src/api/Server.ts | 3 + src/api/middlewares/TestClient.ts | 15 +++- src/api/util/TestClientPatcher.ts | 107 +++++++++++++++++++++++++++ src/api/util/index.ts | 2 + 15 files changed, 304 insertions(+), 167 deletions(-) delete mode 100644 assets/fosscord-login.css create mode 100644 assets/private/icons/custom/.gitkeep create mode 100644 assets/private/icons/homeIcon.path create mode 100644 scripts/patches/applyPatches.js create mode 100644 scripts/patches/mkPatches.js create mode 100644 scripts/patches/prepWS.js create mode 100644 scripts/patches/resetWS.js create mode 100644 src/api/util/TestClientPatcher.ts diff --git a/.gitignore b/.gitignore index a582a2f3..8631a69d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ yarn.lock dbconf.json migrations.db + +assets/cache_src/ diff --git a/assets/fosscord-login.css b/assets/fosscord-login.css deleted file mode 100644 index 975bf908..00000000 --- a/assets/fosscord-login.css +++ /dev/null @@ -1,68 +0,0 @@ -/* replace tos acceptance popup */ -#app-mount > div:nth-child(7) > div > div > div.tooltipContent-bqVLWK { - visibility: hidden; -} -#app-mount > div:nth-child(7) > div > div > div.tooltipContent-bqVLWK::after { - visibility: visible; - display: block; - content: "You need to agree to this instance's rules to continue"; - margin-top: -32px; -} -/* replace login header */ -#app-mount > div.app-1q1i1E > div > div > div > div > form > div > div > div.mainLoginContainer-1ddwnR > h3 { - visibility: hidden; -} -h3.title-jXR8lp.marginBottom8-AtZOdT.base-1x0h_U.size24-RIRrxO::after { - margin-top: -32px; - content: "Welcome to Fosscord!"; - visibility: visible; - display: block; -} - -/* Logo in top left when bg removed */ -#app-mount > div.app-1q1i1E > div > a { - /* replace me: original dimensions: 130x36 */ - background: url(https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Gradient.svg); - width: 130px; - height: 23px; - background-size: contain; -} - -/* replace TOS text */ - -#app-mount - > div.app-1q1i1E - > div - > div - > div - > form - > div - > div - > div.flex-1xMQg5.flex-1O1GKY.horizontal-1ae9ci.horizontal-2EEEnY.flex-1O1GKY.directionRow-3v3tfG.justifyStart-2NDFzi.alignCenter-1dQNNs.noWrap-3jynv6.marginTop20-3TxNs6 - > label - > div.label-cywgfr.labelClickable-11AuB8.labelForward-1wfipV - > * { - visibility: hidden; -} - -#app-mount - > div.app-1q1i1E - > div - > div - > div - > form - > div - > div - > div.flex-1xMQg5.flex-1O1GKY.horizontal-1ae9ci.horizontal-2EEEnY.flex-1O1GKY.directionRow-3v3tfG.justifyStart-2NDFzi.alignCenter-1dQNNs.noWrap-3jynv6.marginTop20-3TxNs6 - > label - > div.label-cywgfr.labelClickable-11AuB8.labelForward-1wfipV::after { - visibility: visible; - content: "I have read and agree with the rules set by this instance."; - display: block; - margin-top: -16px; -} - -/* shrink login box to same size as register */ -.authBoxExpanded-2jqaBe { - width: 480px !important; -} diff --git a/assets/fosscord.css b/assets/fosscord.css index fa503d39..ba54b1cf 100644 --- a/assets/fosscord.css +++ b/assets/fosscord.css @@ -1,92 +1,4 @@ /* loading spinner */ -#app-mount > div.app-1q1i1E > div.container-16j22k.fixClipping-3qAKRb > div.content-1-zrf2 > video { - filter: opacity(1); - background: url("http://www.clipartbest.com/cliparts/7ca/6Rr/7ca6RrLAi.gif"); - background-size: contain; - /* width: 64px; - height: 64px; */ - padding-bottom: 64px; - background-repeat: no-repeat; -} - -/* home button icon */ -#app-mount - > div.app-1q1i1E - > div - > div.layers-3iHuyZ.layers-3q14ss - > div - > div - > nav - > ul - > div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih - > div.tutorialContainer-2sGCg9 - > div - > div.listItemWrapper-KhRmzM - > div - > svg - > foreignObject - > div - > div { - background-image: url(https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Icon-Rounded-Subtract.svg); - background-size: contain; - border-radius: 50%; -} - -#app-mount - > div.app-1q1i1E - > div - > div.layers-3iHuyZ.layers-3q14ss - > div - > div - > nav - > ul - > div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih - > div.tutorialContainer-2sGCg9 - > div - > div.listItemWrapper-KhRmzM - > div - > svg - > foreignObject - > div - > div, -#app-mount - > div.app-1q1i1E - > div - > div.layers-3iHuyZ.layers-3q14ss - > div - > div - > nav - > ul - > div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih - > div.tutorialContainer-2sGCg9 - > div - > div.listItemWrapper-KhRmzM - > div - > svg - > foreignObject - > div - > div:hover { - background-color: white; -} -/* Login QR */ -#app-mount > div.app-1q1i1E > div > div > div > div > form > div > div > div.transitionGroup-aR7y1d.qrLogin-1AOZMt, -#app-mount > div.app-1q1i1E > div > div > div > div > form > div > div > div.verticalSeparator-3huAjp, -/* Remove login bg */ -#app-mount > div.app-1q1i1E > div > svg, -/* Download bar */ -#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > div > div.notice-3bPHh-.colorDefault-22HBa0, -/* Connection problem links */ -#app-mount > div.app-1q1i1E > div.container-16j22k.fixClipping-3qAKRb > div.problems-3mgf6w.slideIn-sCvzGz > div:nth-child(2), -/* Downloads button */ -#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > nav > ul > div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih > div:nth-child(7) > div.listItemWrapper-KhRmzM > div > svg > foreignObject > div, -#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > nav > ul > div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih > div:nth-child(6) > div, -/* help button */ -#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > div > div.content-98HsJk > div.chat-3bRxxu > section > div.toolbar-1t6TWx > a, -/* download button start of guild */ -#chat-messages-899316648933185083 > div > div > div:nth-child(5), -/* Thread permissions etc popups */ -#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > div > div.content-98HsJk > div.sidebar-2K8pFh.hasNotice-1XRy4h > nav > div.container-3O_wAf, -/* home button icon */ -#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > nav > ul > div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih > div.tutorialContainer-2sGCg9 > div > div.listItemWrapper-KhRmzM > div > svg > foreignObject > div > div > svg { - display: none; -} +:root { + --brand-hue: 22; +} \ No newline at end of file diff --git a/assets/index.html b/assets/index.html index 4513d0d2..0cd90c8e 100644 --- a/assets/index.html +++ b/assets/index.html @@ -5,7 +5,6 @@ Discord Test Client - @@ -28,20 +27,21 @@ INVITE_HOST: `${location.hostname}/invite`, GUILD_TEMPLATE_HOST: "${location.host}", GIFT_CODE_HOST: "${location.hostname}", - RELEASE_CHANNEL: "stable", MARKETING_ENDPOINT: "//discord.com", BRAINTREE_KEY: "production_5st77rrc_49pp2rp4phym7387", STRIPE_KEY: "pk_live_CUQtlpQUF0vufWpnpUmQvcdi", NETWORKING_ENDPOINT: "//router.discordapp.net", RTC_LATENCY_ENDPOINT: "//${location.hostname}/rtc", ACTIVITY_APPLICATION_HOST: "discordsays.com", - PROJECT_ENV: "production", REMOTE_AUTH_ENDPOINT: "//localhost:3020", SENTRY_TAGS: { buildId: "75e36d9", buildType: "normal" }, MIGRATION_SOURCE_ORIGIN: "https://${location.hostname}", MIGRATION_DESTINATION_ORIGIN: "https://${location.hostname}", HTML_TIMESTAMP: Date.now(), - ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0" + ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0", + SENTRY_TAGS: { instance: document.location.host }, + PROJECT_ENV: "development", + RELEASE_CHANNEL: "staging", }; GLOBAL_ENV.MEDIA_PROXY_ENDPOINT = location.protocol + "//" + GLOBAL_ENV.CDN_HOST; const localStorage = window.localStorage; diff --git a/assets/private/icons/custom/.gitkeep b/assets/private/icons/custom/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/assets/private/icons/homeIcon.path b/assets/private/icons/homeIcon.path new file mode 100644 index 00000000..b4b062ca --- /dev/null +++ b/assets/private/icons/homeIcon.path @@ -0,0 +1 @@ +M 0,0 47.999993,2.7036528e-4 C 48.001796,3.3028172 47.663993,6.5968018 46.991821,9.8301938 43.116101,28.454191 28.452575,43.116441 9.8293509,46.992163 6.5960834,47.664163 3.3023222,48.001868 0,47.999992 Z m 9.8293509,28.735114 v 9.248482 C 22.673599,33.047696 32.857154,22.749268 37.63852,9.829938 H 9.8293509 v 8.679899 H 22.931288 c -3.554489,3.93617 -7.735383,7.257633 -12.373436,9.829938 -0.241031,0.133684 -0.483864,0.265492 -0.7285011,0.395339 z \ No newline at end of file diff --git a/fosscord-server.code-workspace b/fosscord-server.code-workspace index 56450f85..a9b15856 100644 --- a/fosscord-server.code-workspace +++ b/fosscord-server.code-workspace @@ -16,6 +16,8 @@ "settings": { "files.exclude": { "*.ansi": true, + "**/cache": true, + "**/cache_src": true } }, "launch": { @@ -32,6 +34,12 @@ "name": "Run Fosscord with debugger (kitty)", "request": "launch", "type": "node-terminal" + }, + { + "command": "[ \"$(basename $PWD)\" != \"fosscord-server\" ] && cd ..; $(ps -o comm= $PPID) assets/cache", + "name": "Open testclient patch workspace", + "request": "launch", + "type": "node-terminal" } ] } diff --git a/scripts/patches/applyPatches.js b/scripts/patches/applyPatches.js new file mode 100644 index 00000000..66524ede --- /dev/null +++ b/scripts/patches/applyPatches.js @@ -0,0 +1,24 @@ +const { execSync } = require("child_process"); +const path = require("path"); +const fs = require("fs"); +const { argv, stdout, exit } = require("process"); +const { execIn, parts, getDirs, walk, sanitizeVarName } = require("../utils"); + +//apply patches +const patchDir = path.join(__dirname, "..", "..", "assets", "testclient_patches"); +const targetDir = path.join(__dirname, "..", "..", "assets", "cache"); +const files = fs.readdirSync(patchDir); +files.forEach((file) => { + const filePath = path.join(patchDir, file); + const stats = fs.statSync(filePath); + if (stats.isFile()) { + const ext = path.extname(file); + if (ext === ".patch") { + execSync(`git apply ${filePath}`, { + cwd: targetDir, + maxBuffer: 1024 * 1024 * 10, + }); + console.log(`Applied patch ${file} to ${newFilePath}`); + } + } +}); \ No newline at end of file diff --git a/scripts/patches/mkPatches.js b/scripts/patches/mkPatches.js new file mode 100644 index 00000000..1e1e0475 --- /dev/null +++ b/scripts/patches/mkPatches.js @@ -0,0 +1,40 @@ +const { execSync } = require("child_process"); +const path = require("path"); +const fs = require("fs"); +const { argv, stdout, exit } = require("process"); +const { execIn, parts, getDirs, walk, sanitizeVarName } = require("../utils"); + +//generate git patch for each file in assets/cache +const srcDir = path.join(__dirname, "..", "..", "assets", "cache"); +const destDir = path.join(__dirname, "..", "..", "assets", "cache_src"); +const patchDir = path.join(__dirname, "..", "..", "assets", "testclient_patches"); +if(!fs.existsSync(patchDir)) fs.mkdirSync(patchDir); +const files = fs.readdirSync(srcDir); +files.forEach((file) => { + const filePath = path.join(srcDir, file); + const stats = fs.statSync(filePath); + if (stats.isFile()) { + const ext = path.extname(file); + if (ext === ".js" || ext === ".css") { + const newFilePath = path.join(destDir, file); + //check if file has been modified + let patch; + try { + let es = execSync(`diff -du --speed-large-files --horizon-lines=0 ${newFilePath} ${filePath}`, { + maxBuffer: 1024 * 1024 * 10, + }).toString(); + patch=""; + } catch (e) { + patch = e.stdout.toString().replaceAll(path.join(destDir, file), file).replaceAll(path.join(srcDir, file), file); + } + if (patch.length > 0) { + //generate patch; + fs.writeFileSync(path.join(patchDir, file + ".patch"), patch); + console.log(`Generated patch for ${file}: ${patch.length} bytes, ${patch.split("\n").length} lines, ${patch.split("\n").filter((x) => x.startsWith("+")).length} additions, ${patch.split("\n").filter((x) => x.startsWith("-")).length} deletions`); + } + else { + //console.log(`No changes for ${file}`); + } + } + } +}); \ No newline at end of file diff --git a/scripts/patches/prepWS.js b/scripts/patches/prepWS.js new file mode 100644 index 00000000..bc4d0a9a --- /dev/null +++ b/scripts/patches/prepWS.js @@ -0,0 +1,75 @@ +const { execSync } = require("child_process"); +const path = require("path"); +const fs = require("fs"); +const { argv, stdout, exit } = require("process"); +const { execIn, parts, getDirs, walk, sanitizeVarName } = require("../utils"); + +//copy all js and css files from assets/cache to assets/dist +const srcDir = path.join(__dirname, "..", "..", "assets", "cache"); +const destDir = path.join(__dirname, "..", "..", "assets", "cache_src"); +if(!fs.existsSync(destDir)) fs.mkdirSync(destDir); +const files = fs.readdirSync(srcDir); +files.forEach((file) => { + const filePath = path.join(srcDir, file); + const stats = fs.statSync(filePath); + if (stats.isFile()) { + const ext = path.extname(file); + if (ext === ".js" || ext === ".css") { + const newFilePath = path.join(destDir, file); + if(!fs.existsSync(newFilePath)) { + fs.copyFileSync(filePath, newFilePath); + console.log(`Copied ${file} to ${newFilePath}`); + } + } + } +}); +if(!fs.existsSync(path.join(srcDir, ".vscode"))) fs.mkdirSync(path.join(srcDir, ".vscode")); +fs.writeFileSync(path.join(srcDir, ".vscode", "settings.json"), JSON.stringify({ + "codemetrics.basics.DecorationModeEnabled": false, + "codemetrics.basics.CodeLensEnabled": false, + "editor.codeLens": false, + //"editor.minimap.enabled": false, + "codemetrics.basics.MetricsForArrowFunctionsToggled": false, + "codemetrics.basics.MetricsForClassDeclarationsToggled": false, + "codemetrics.basics.MetricsForConstructorsToggled": false, + "codemetrics.basics.MetricsForEnumDeclarationsToggled": false, + "codemetrics.basics.MetricsForFunctionExpressionsToggled": false, + "codemetrics.basics.MetricsForFunctionDeclarationsToggled": false, + "codemetrics.basics.MetricsForMethodDeclarationsToggled": false, + "codemetrics.basics.OverviewRulerModeEnabled": false, + "editor.fontFamily": "'JetBrainsMono Nerd Font', 'JetBrainsMono', 'Droid Sans Mono', 'monospace', monospace", + "editor.accessibilityPageSize": 1, + "editor.accessibilitySupport": "off", + "editor.autoClosingDelete": "never", + //"editor.autoIndent": "none", + //"editor.colorDecorators": false, + "editor.comments.ignoreEmptyLines": false, + "editor.copyWithSyntaxHighlighting": false, + "editor.comments.insertSpace": false, + "editor.detectIndentation": false, + "editor.dragAndDrop": false, + "editor.dropIntoEditor.enabled": false, + "editor.experimental.pasteActions.enabled": false, + "editor.guides.highlightActiveIndentation": false, + "color-highlight.enable": false, + "gitlens.blame.highlight.locations": [ + "gutter" + ], + "todohighlight.isEnable": false, + "todohighlight.maxFilesForSearch": 1, + "editor.maxTokenizationLineLength": 1200, + "editor.minimap.maxColumn": 140, + "explorer.openEditors.visible": 0, + "editor.fontLigatures": false, + "files.exclude": { + "*.mp3": true, + "*.png": true, + "*.svg": true, + "*.webm": true, + "*.webp": true, + "*.woff2": true, + "**/.vscode/": true + }, + "editor.guides.bracketPairs": true +}, null, 4)); +console.log(`Workspace prepared at ${srcDir}!`); \ No newline at end of file diff --git a/scripts/patches/resetWS.js b/scripts/patches/resetWS.js new file mode 100644 index 00000000..d34b74fd --- /dev/null +++ b/scripts/patches/resetWS.js @@ -0,0 +1,24 @@ +const { execSync } = require("child_process"); +const path = require("path"); +const fs = require("fs"); +const { argv, stdout, exit } = require("process"); +const { execIn, parts, getDirs, walk, sanitizeVarName } = require("../utils"); + +//copy all js and css files from assets/cache_src to assets/cache +const srcDir = path.join(__dirname, "..", "..", "assets", "cache_src"); +const destDir = path.join(__dirname, "..", "..", "assets", "cache"); +if(!fs.existsSync(destDir)) fs.mkdirSync(destDir); +const files = fs.readdirSync(srcDir); +files.forEach((file) => { + const filePath = path.join(srcDir, file); + const stats = fs.statSync(filePath); + if (stats.isFile()) { + const ext = path.extname(file); + if (ext === ".js" || ext === ".css") { + const newFilePath = path.join(destDir, file); + fs.rmSync(newFilePath); + fs.copyFileSync(filePath, newFilePath); + console.log(`Copied ${file} to ${newFilePath}`); + } + } +}); \ No newline at end of file diff --git a/src/api/Server.ts b/src/api/Server.ts index e92335a5..560014d0 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -11,6 +11,7 @@ import { initRateLimits } from "./middlewares/RateLimit"; import TestClient from "./middlewares/TestClient"; import { initTranslation } from "./middlewares/Translation"; import { initInstance } from "./util/handlers/Instance"; +import fs from "fs"; export interface FosscordServerOptions extends ServerOptions {} @@ -42,6 +43,8 @@ export class FosscordServer extends Server { this.app.use( morgan("combined", { skip: (req, res) => { + if(req.path.endsWith(".map")) return true; + if(req.path.includes("/assets/") && !fs.existsSync(path.join(__dirname, "..", "..", "..", "assets", req.path.split("/")[0].split('?')[0]))) return true; let skip = !(process.env["LOG_REQUESTS"]?.includes(res.statusCode.toString()) ?? false); if (process.env["LOG_REQUESTS"]?.charAt(0) == "-") skip = !skip; return skip; diff --git a/src/api/middlewares/TestClient.ts b/src/api/middlewares/TestClient.ts index 3afd0339..9090840e 100644 --- a/src/api/middlewares/TestClient.ts +++ b/src/api/middlewares/TestClient.ts @@ -6,7 +6,9 @@ import path from "path"; import { green } from "picocolors"; import ProxyAgent from "proxy-agent"; import { AssetCacheItem } from "../util/entities/AssetCacheItem"; +import { patchFile } from ".."; +const prettier = require("prettier"); const AssetsPath = path.join(__dirname, "..", "..", "..", "assets"); export default function TestClient(app: Application) { @@ -39,7 +41,7 @@ export default function TestClient(app: Application) { let response: FetchResponse; let buffer: Buffer; let assetCacheItem: AssetCacheItem = new AssetCacheItem(req.params.file); - if (newAssetCache.has(req.params.file)) { + if (newAssetCache.has(req.params.file) && fs.existsSync(newAssetCache.get(req.params.file)!.FilePath)) { assetCacheItem = newAssetCache.get(req.params.file)!; assetCacheItem.Headers.forEach((value: any, name: any) => { res.set(name, value); @@ -56,16 +58,21 @@ export default function TestClient(app: Application) { ...req.headers } }); - //set cache info assetCacheItem.Headers = Object.fromEntries(stripHeaders(response.headers)); - assetCacheItem.FilePath = path.join(assetCacheDir, req.params.file); assetCacheItem.Key = req.params.file; //add to cache and save newAssetCache.set(req.params.file, assetCacheItem); + + if(response.status != 200) { + return res.status(404).send("Not found"); + } + assetCacheItem.FilePath = path.join(assetCacheDir, req.params.file); + if(!fs.existsSync(assetCacheDir)) + fs.mkdirSync(assetCacheDir); fs.writeFileSync(path.join(assetCacheDir, "index.json"), JSON.stringify(Object.fromEntries(newAssetCache), null, 4)); //download file - fs.writeFileSync(assetCacheItem.FilePath, await response.buffer()); + fs.writeFileSync(assetCacheItem.FilePath, /.*\.(js|css)/.test(req.params.file) ? patchFile(assetCacheItem.FilePath, (await response.buffer()).toString()) : await response.buffer()); } assetCacheItem.Headers.forEach((value: string, name: string) => { diff --git a/src/api/util/TestClientPatcher.ts b/src/api/util/TestClientPatcher.ts new file mode 100644 index 00000000..2e9bfafe --- /dev/null +++ b/src/api/util/TestClientPatcher.ts @@ -0,0 +1,107 @@ +import path from "path"; +import fs from "fs"; + +console.log('[TestClient] Loading private assets...'); + +const privateAssetsRoot = path.join(__dirname, "..", "..", "..", "assets", "private"); +const iconsRoot = path.join(privateAssetsRoot, "icons"); +const icons = new Map(); + +fs.readdirSync(iconsRoot).forEach(file => { + const fileName = path.basename(file); + //check if dir + if(fs.lstatSync(path.join(iconsRoot, file)).isDirectory()){ + return; + } + icons.set(fileName,fs.readFileSync(path.join(iconsRoot,file)) as Buffer); +}); + +fs.readdirSync(path.join(iconsRoot, "custom")).forEach(file => { + const fileName = path.basename(file); + if(fs.lstatSync(path.join(iconsRoot,"custom", file)).isDirectory()){ + return; + } + icons.set(fileName,fs.readFileSync(path.join(iconsRoot,"custom",file)) as Buffer); +}); + +console.log('[TestClient] Patcher ready!'); + +export function patchFile(filePath: string, content: string): string { + console.log(`[TestClient] Patching ${filePath}`); + let startTime = Date.now(); + + content = prettier(filePath, content); + content = autoPatch(filePath, content); + + console.log(`[TestClient] Patched ${filePath} in ${Date.now() - startTime}ms`); + return content; +} +function prettier(filePath: string, content: string): string{ + let prettier = require("prettier"); + let parser; + filePath = filePath.toLowerCase().split('?')[0]; + if(filePath.endsWith(".js")) { + parser = "babel"; + } else if (filePath.endsWith(".ts")){ + parser = "typescript"; + } else if(filePath.endsWith(".css")){ + parser = "css"; + } else if(filePath.endsWith(".json")){ + parser = "json"; + } + else { + console.log(`[TestClient] Skipping prettier for ${filePath}, unknown file type!`); + return content; + } + content = prettier.format(content, { + tabWidth: 4, + useTabs: true, + printWidth: 140, + trailingComma: "none", + parser + }); + console.log(`[TestClient] Prettified ${filePath}!`); + return content; +} + +function autoPatch(filePath: string, content: string): string{ + //remove nitro references + content = content.replace(/Discord Nitro/g, "Fosscord Premium"); + content = content.replace(/"Nitro"/g, "\"Premium\""); + content = content.replace(/Nitro /g, "Premium "); + content = content.replace(/ Nitro/g, " Premium"); + content = content.replace(/\[Nitro\]/g, "[Premium]"); + content = content.replace(/\*Nitro\*/g, "*Premium*"); + content = content.replace(/\"Nitro \. /g, "\"Premium. "); + + //remove discord references + content = content.replace(/ Discord /g, " Fosscord "); + content = content.replace(/Discord /g, "Fosscord "); + content = content.replace(/ Discord/g, " Fosscord"); + content = content.replace(/Discord Premium/g, "Fosscord Premium"); + content = content.replace(/Discord Nitro/g, "Fosscord Premium"); + content = content.replace(/Discord's/g, "Fosscord's"); + //content = content.replace(/DiscordTag/g, "FosscordTag"); + content = content.replace(/\*Discord\*/g, "*Fosscord*"); + + //change some vars + content = content.replace('dsn: "https://fa97a90475514c03a42f80cd36d147c4@sentry.io/140984"', "dsn: (/true/.test(localStorage.sentryOptIn)?'https://6bad92b0175d41a18a037a73d0cff282@sentry.thearcanebrony.net/12':'')"); + content = content.replace('t.DSN = "https://fa97a90475514c03a42f80cd36d147c4@sentry.io/140984"', "t.DSN = (/true/.test(localStorage.sentryOptIn)?'https://6bad92b0175d41a18a037a73d0cff282@sentry.thearcanebrony.net/12':'')"); + content = content.replace('--brand-experiment: hsl(235, calc(var(--saturation-factor, 1) * 85.6%), 64.7%);', '--brand-experiment: hsl(var(--brand-hue), calc(var(--saturation-factor, 1) * 85.6%), 50%);'); + content = content.replaceAll(/--brand-experiment-(\d{1,4}): hsl\(235/g, '--brand-experiment-\$1: hsl(var(--brand-hue)') + + //logos + content = content.replace(/d: "M23\.0212.*/, `d: "${icons.get("homeIcon.path")!.toString()}"`); + content = content.replace('width: n, height: o, viewBox: "0 0 28 20"', 'width: 48, height: 48, viewBox: "0 0 48 48"'); + + //undo webpacking + // - booleans + content = content.replace(/!0/g, "true"); + content = content.replace(/!1/g, "false"); + // - real esmodule defs + content = content.replace(/Object.defineProperty\((.), "__esModule", { value: (.*) }\);/g, '\$1.__esModule = \$2;'); + + + console.log(`[TestClient] Autopatched ${path.basename(filePath)}!`); + return content; +} \ No newline at end of file diff --git a/src/api/util/index.ts b/src/api/util/index.ts index d06860cd..f01c2f43 100644 --- a/src/api/util/index.ts +++ b/src/api/util/index.ts @@ -8,3 +8,5 @@ export * from "./utility/ipAddress"; export * from "./utility/passwordStrength"; export * from "./utility/RandomInviteID"; export * from "./utility/String"; +export * from "./utility/captcha"; +export * from "./TestClientPatcher"; \ No newline at end of file