test/helpers/installed.js (2413 bytes)
1 "use strict"; 2 // Loads code from the installed BetterDiscord and BDFDB, so the tests check the versions that Discord runs. 3 const fs = require("node:fs"); 4 const path = require("node:path"); 5 6 const BD_FOLDER = path.join(process.env.APPDATA || "", "BetterDiscord"); 7 const BD_ASAR = path.join(BD_FOLDER, "data", "betterdiscord.asar"); 8 const BDFDB_FILE = path.join(BD_FOLDER, "plugins", "0BDFDB.plugin.js"); 9 10 /** Reads one top-level file from an asar archive. */ 11 function readAsarFile(archive, name) { 12 const buffer = fs.readFileSync(archive); 13 const headerPickleSize = buffer.readUInt32LE(4); 14 const jsonLength = buffer.readUInt32LE(12); 15 const header = JSON.parse(buffer.toString("utf8", 16, 16 + jsonLength)); 16 const entry = header.files[name]; 17 const start = 8 + headerPickleSize + Number(entry.offset); 18 return buffer.toString("utf8", start, start + entry.size); 19 } 20 21 /** Returns the index of the brace that closes the brace at openIndex. */ 22 function closingBrace(text, openIndex) { 23 let depth = 0; 24 for (let i = openIndex; i < text.length; i++) { 25 if (text[i] === "{") depth++; 26 else if (text[i] === "}" && --depth === 0) return i; 27 } 28 throw new Error("no closing brace"); 29 } 30 31 /** Returns the internal Patcher class of the installed BetterDiscord. */ 32 function loadBdPatcher(logger) { 33 const source = readAsarFile(BD_ASAR, "betterdiscord.js"); 34 const marker = source.indexOf("static getPatchesByCaller("); 35 if (marker < 0) throw new Error("BetterDiscord Patcher not found"); 36 const classStart = source.lastIndexOf("=class{", marker) + 1; 37 const body = source.slice(classStart, closingBrace(source, classStart + "class".length) + 1); 38 // The minified class uses g (logger), w (module cache), and ae (module search) from its bundle. 39 return new Function("g", "w", "ae", `return ${body};`)(logger, {}, () => null); 40 } 41 42 /** Returns BDFDB.TimeUtils.suppress from the installed BDFDB library. */ 43 function loadBdfdbSuppress(logUtils) { 44 const source = fs.readFileSync(BDFDB_FILE, "utf8"); 45 const match = /BDFDB\.TimeUtils\.suppress = (function \(callback, strings, config\) \{[\s\S]*?\n\t*\}\};)/.exec(source); 46 if (!match) throw new Error("BDFDB TimeUtils.suppress not found"); 47 return new Function("BDFDB", `return ${match[1].slice(0, -1)}`)({LogUtils: logUtils}); 48 } 49 50 module.exports = {BD_ASAR, BDFDB_FILE, loadBdPatcher, loadBdfdbSuppress};