import {bdConfig, minSupportedVersion, bbdVersion, settingsCookie, bdpluginErrors, bdthemeErrors, bbdChangelog, defaultCookie, currentDiscordVersion, defaultRPC, settingsRPC} from "../0globals"; import Utils from "./utils"; import BDV2 from "./v2"; import settingsPanel from "./settingsPanel"; import pluginModule from "./pluginModule"; import themeModule from "./themeModule"; import DataStore from "./dataStore"; import WebpackModules from "./webpackModules"; import DOM from "./domtools"; import BDLogo from "../ui/bdLogo"; import TooltipWrap from "../ui/tooltipWrap"; import LightcordLogo from "../svg/lightcord"; import PluginCertifier from "./pluginCertifier"; import distant, { uuidv4 } from "./distant"; import EmojiModule from "./emojiModule" import {remote as electron} from "electron" import v2 from "./v2"; import webpackModules from "./webpackModules"; function Core() { // Object.assign(bdConfig, __non_webpack_require__(DataStore.configFile)); // this.init(); } Core.prototype.setConfig = function(config) { Object.assign(bdConfig, config); }; Core.prototype.init = async function() { if (!Array.prototype.flat) { Utils.alert("Not Supported", "BetterDiscord v" + bbdVersion + " does not support this old version (" + currentDiscordVersion + ") of Discord. Please update your Discord installation before proceeding."); return; } if (bdConfig.version < minSupportedVersion) { Utils.alert("Not Supported", "BetterDiscord v" + bdConfig.version + " (your version)" + " is not supported by the latest js (" + bbdVersion + ").

Please download the latest version from GitHub"); return; } if (window.ED) { Utils.alert("Not Supported", "BandagedBD does not work with EnhancedDiscord. Please uninstall one of them."); return; } if (window.WebSocket && window.WebSocket.name && window.WebSocket.name.includes("Patched")) { Utils.alert("Not Supported", "BandagedBD does not work with Powercord. Please uninstall one of them."); return; } Utils.suppressErrors(this.patchAttributes.bind(this), "LC Plugin Certifier Patch")(); /* const latestLocalVersion = bdConfig.updater ? bdConfig.updater.LatestVersion : bdConfig.latestVersion; if (latestLocalVersion > bdConfig.version) { Utils.showConfirmationModal("Update Available", [`There is an update available for BandagedBD's Injector (${latestLocalVersion}).`, "You can either update and restart now, or later."], { confirmText: "Update Now", cancelText: "Maybe Later", onConfirm: async () => { const onUpdateFailed = () => {Utils.alert("Could Not Update", `Unable to update automatically, please download the installer and reinstall normally.

Download Installer`);}; try { const didUpdate = await this.updateInjector(); if (!didUpdate) return onUpdateFailed(); const app = require("electron").remote.app; app.relaunch(); app.exit(); } catch (err) { onUpdateFailed(); } } }); }*/ Utils.log("Startup", "Initializing Settings"); this.initSettings(); await this.checkForGuilds(); BDV2.initialize(); Utils.log("Startup", "Updating Settings"); settingsPanel.initializeSettings(); Utils.log("Startup", "Loading Plugins"); await pluginModule.loadPlugins(); Utils.log("Startup", "Loading Themes"); await themeModule.loadThemes(); DOM.addStyle("customcss", atob(DataStore.getBDData("bdcustomcss"))); window.addEventListener("beforeunload", function() { if (settingsCookie["bda-dc-0"]) document.querySelector(".btn.btn-disconnect").click(); }); PluginCertifier.start() Utils.log("Startup", "Removing Loading Icon"); if (document.getElementsByClassName("bd-loaderv2").length) document.getElementsByClassName("bd-loaderv2")[0].remove(); Utils.log("Startup", "Initializing Main Observer"); this.initObserver(); // Show loading errors if (settingsCookie["fork-ps-1"]) { Utils.log("Startup", "Collecting Startup Errors"); Utils.showContentErrors({plugins: bdpluginErrors, themes: bdthemeErrors}); } const previousVersion = DataStore.getBDData("version"); if (bbdVersion > previousVersion) { if (bbdChangelog) this.showChangelogModal(bbdChangelog); DataStore.setBDData("version", bbdVersion); } EmojiModule.start() Utils.suppressErrors(this.patchSocial.bind(this), "BD Social Patch")(); Utils.suppressErrors(this.patchGuildPills.bind(this), "BD Guild Pills Patch")(); Utils.suppressErrors(this.patchGuildListItems.bind(this), "BD Guild List Items Patch")(); Utils.suppressErrors(this.patchGuildSeparator.bind(this), "BD Guild Separator Patch")(); Utils.suppressErrors(this.patchMessageHeader.bind(this), "BD Badge Chat Patch")(); Utils.suppressErrors(this.patchMemberList.bind(this), "BD Badge Member List Patch")(); Utils.suppressErrors(this.patchAttachment.bind(this), "LC Plugin Certifier Patch")(); if(bdConfig.haveInstalledDefault){ let alert = Utils.alert("First Installation", "As it is the first time you install Lightcord, We added two default themes and one default plugin in your plugin/theme folder. Check it in the Plugin/Theme settings.") await new Promise((resolve) => { alert.onClose(resolve) }) } const logo = document.querySelector("#app-mount > div.typeWindows-1za-n7.withFrame-haYltI.titleBar-AC4pGV.horizontalReverse-3tRjY7.flex-1O1GKY.directionRowReverse-m8IjIq.justifyStart-2NDFzi.alignStretch-DpGPf3.da-typeWindows.da-withFrame.da-titleBar.da-horizontalReverse.da-flex.da-directionRowReverse.da-justifyStart.da-alignStretch > div.wordmarkWindows-1v0lYD.wordmark-2iDDfm.da-wordmarkWindows.da-wordmark") if(logo){ logo.style.top = "3px" logo.innerHTML = ` ` } }; Core.prototype.patchAttributes = async function() { let attribsPatchs = [] this.cancelPatchAttributes = function() { attribsPatchs.forEach(e => e()) } while(!v2.MessageComponent)await new Promise(resolve => setTimeout(resolve, 100)) window.Lightcord.Api.ensureExported(e => e.default && e.default.displayName && e.default.displayName.includes("UserPopout")) .then(UserPopout => { console.log(UserPopout) const render = UserPopout.default.prototype.render UserPopout.default.prototype.render = function(){ const returnValue = render.call(this, ...arguments) console.log(returnValue, this.props) return returnValue } }) attribsPatchs.push(Utils.monkeyPatch(v2.MessageComponent, "default", {after: (data) => { if(data.methodArguments[0].childrenMessageContent.props.message){ // this can be a blocked message (not opened) data.returnValue.props["data-message-id"] = data.methodArguments[0].childrenMessageContent.props.message.id } }})) /* attribsPatchs.push(Utils.monkeyPatch(v2.MessageComponent, "default", {after: (data) => { data.returnValue.props["message-id"] = data.methodArguments[0].childrenMessageContent.props.message.id }}))*/ } Core.prototype.checkForGuilds = function() { let timesChecked = 0; return new Promise(resolve => { const checkForGuilds = function() { const wrapper = BDV2.guildClasses.wrapper.split(" ")[0]; if (document.querySelectorAll(`.${wrapper}`).length > 0) timesChecked++; const guild = BDV2.guildClasses.listItem.split(" ")[0]; const blob = BDV2.guildClasses.blobContainer.split(" ")[0]; if (document.querySelectorAll(`.${wrapper} .${guild} .${blob}`).length > 0) return resolve(bdConfig.deferLoaded = true); else if (timesChecked >= 50) return resolve(bdConfig.deferLoaded = true); setTimeout(checkForGuilds, 100); }; if (document.readyState != "loading") setTimeout(checkForGuilds, 100); document.addEventListener("DOMContentLoaded", () => {setTimeout(checkForGuilds, 100);}); }); }; Core.prototype.injectExternals = async function() { await DOM.addScript("ace-script", "https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js"); if (window.require.original) window.require = window.require.original; }; Core.prototype.initSettings = function () { DataStore.initialize(); if(!DataStore.getSettingGroup("settings") && !DataStore.getSettingGroup("rpc")){ if (!DataStore.getSettingGroup("settings")) { Object.assign(settingsCookie, defaultCookie); } if (!DataStore.getSettingGroup("rpc")) { Object.assign(settingsRPC, defaultRPC); } settingsPanel.saveSettings(); } else { settingsPanel.loadSettings(); for (const setting in defaultCookie) { if (settingsCookie[setting] == undefined) { settingsCookie[setting] = defaultCookie[setting]; settingsPanel.saveSettings(); } } } window.Lightcord.Api.ensureExported(e => e.default && e.default.prototype && e.default.prototype.getPredicateSections) .then(settingModule => { let getPredicateSections = settingModule.default.prototype.getPredicateSections settingModule.default.prototype.getPredicateSections = function(){ let result = getPredicateSections.call(this, ...arguments) if(result[1].section === "My Account"){ // user settings, not guild settings let poped = [] poped.push(result.pop()) poped.push(result.pop()) poped.push(result.pop()) poped.push(result.pop()) result.push(...settingsPanel.renderSidebar()) while(poped[0]){ result.push(poped.pop()) } } return result } }) }; let classNameLayer let classNameSocialLinks let classNameModal Core.prototype.initObserver = function () { const mainObserver = new MutationObserver((mutations) => { for (let i = 0, mlen = mutations.length; i < mlen; i++) { const mutation = mutations[i]; if (typeof pluginModule !== "undefined") pluginModule.rawObserver(mutation); // if there was nothing added, skip if (!mutation.addedNodes.length || !(mutation.addedNodes[0] instanceof Element)) continue; const node = mutation.addedNodes[0]; if(!classNameLayer)classNameLayer = BDModules.get((e) => e.layer && typeof e.layer === "string" && e.animating)[0].layer if(!classNameSocialLinks)classNameSocialLinks = BDModules.get((e) => e.socialLinks && typeof e.socialLinks === "string")[0].socialLinks if(!classNameModal)classNameModal = BDModules.get((e) => e.modal && typeof e.modal === "string" && e.inner && typeof e.inner === "string" && !e.responsiveWidthMobile)[0].modal if (node.classList.contains(classNameLayer)) { if (node.getElementsByClassName("guild-settings-base-section").length) node.setAttribute("layer-id", "server-settings"); if (node.getElementsByClassName(classNameSocialLinks).length) { node.setAttribute("layer-id", "user-settings"); node.setAttribute("id", "user-settings"); } } if (node.parentElement == document.body && node.querySelector("#ace_settingsmenu")) node.id = "ace_settingsmenu_container"; /* if(node.classList.contains(classNameModal)){ try{ /** * @type {Element} *//* const UserProfile = Utils.getNestedProp(node, "childNodes.0.childNodes.0") let user = BDV2.getInternalInstance(node).pendingProps.children.props.children.props.user console.log(user) if(UserProfile && UserProfile.childNodes.length === 2){ let header = UserProfile.childNodes[0].childNodes[0] let children = BDV2.getInternalInstance(header).return.pendingProps.children[1] console.log(children) children.push(BDV2.react.createElement("p", {}, "sltsv")) } }catch(e){ console.error("An error occured in Badge Rendering:", e) } }*/ // Emoji Picker //node.getElementsByClassName("emojiPicker-3m1S-j").length && !node.querySelector(".emojiPicker-3m1S-j").parentElement.classList.contains("animatorLeft-1EQxU0") //if (node.classList.contains(classNameLayer2) && node.getElementsByClassName(classNameEmojiPicker).length && !node.querySelector("."+classNameEmojiPicker).parentElement.classList.contains(classNameAnimatorLeft)) quickEmoteMenu.obsCallback(node); } }); mainObserver.observe(document, { childList: true, subtree: true }); }; Core.prototype.showChangelogModal = function(options = {}) { return Utils.showChangelogModal(options); }; Core.prototype.alert = function(title, content) { return Utils.alert(title, content); }; Core.prototype.patchSocial = function() { if (this.socialPatch) return; const TabBar = WebpackModules.find(m => m.displayName == "TabBar"); const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); if (!TabBar) return; this.socialPatch = Utils.monkeyPatch(TabBar.prototype, "render", {after: (data) => { const children = data.returnValue.props.children; if (!children || !children.length || children.length < 3) return; if (children[children.length - 3].type.displayName !== "Separator") return; if (!children[children.length - 2].type.toString().includes("socialLinks")) return; if (Anchor) { let socialModule1 = BDModules.get(e => e.socialLinks)[0] const original = children[children.length - 2].type; const newOne = function() { const returnVal = original(...arguments); returnVal.props.children.push( BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord"}, BDV2.React.createElement(Anchor, {className: "bd-social-link "+socialModule1.link, href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-social-logo"}) ) ) ); returnVal.props.children.push( BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD"}, BDV2.React.createElement(Anchor, {className: "bd-social-link "+socialModule1.link, href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-social-logo"}) ) ) ); return returnVal; }; children[children.length - 2].type = newOne; } let [ classNameColorMuted, sizes, classNameVersionHash ] = [ BDModules.get(e => e.colorMuted)[0].colorMuted, BDModules.get(e => e.size32)[0], BDModules.get(e => e.versionHash)[0].versionHash ] const versionHash = `(${bdConfig.hash ? bdConfig.hash.substring(0, 7) : bdConfig.branch})`; const additional = [ BDV2.react.createElement("div", {className: `${classNameColorMuted} ${sizes.size12}`}, `Lightcord ${electron.getGlobal("BuildInfo").version} `, BDV2.react.createElement("span", {className: classNameVersionHash+" da-versionHash"}, `(${(electron.getGlobal("BuildInfo").commit || "Unknown").slice(0, 7)})`)), BDV2.react.createElement("div", {className: `${classNameColorMuted} ${sizes.size12}`}, `BBD ${bbdVersion} `, BDV2.react.createElement("span", {className: classNameVersionHash+" da-versionHash"}, versionHash)) ] const originalVersions = children[children.length - 1].type; children[children.length - 1].type = function() { const returnVal = originalVersions(...arguments); returnVal.props.children.splice(1, 0, additional); return returnVal; }; }}); }; const getGuildClasses = function() { const guildsWrapper = WebpackModules.findByProps("wrapper", "unreadMentionsBar"); const guilds = WebpackModules.findByProps("guildsError", "selected"); const pill = WebpackModules.findByProps("blobContainer"); return Object.assign({}, guildsWrapper, guilds, pill); }; Core.prototype.patchGuildListItems = function() { if (this.guildListItemsPatch) return; const GuildClasses = getGuildClasses(); const listItemClass = GuildClasses.listItem.split(" ")[0]; const blobClass = GuildClasses.blobContainer.split(" ")[0]; const reactInstance = BDV2.getInternalInstance(document.querySelector(`.${listItemClass} .${blobClass}`).parentElement); const GuildComponent = reactInstance.return.type; if (!GuildComponent) return; this.guildListItemsPatch = Utils.monkeyPatch(GuildComponent.prototype, "render", {after: (data) => { if (data.returnValue && data.thisObject) { const returnValue = data.returnValue; const guildData = data.thisObject.props; let className = returnValue.props.className className += " bd-guild"; if (guildData.unread) className += " bd-unread"; if (guildData.selected) className += " bd-selected"; if (guildData.audio) className += " bd-audio"; if (guildData.video) className += " bd-video"; if (guildData.badge) className += " bd-badge"; if (guildData.animatable) className += " bd-animatable"; returnValue.props.className = className return returnValue; } }}); }; Core.prototype.patchGuildPills = function() { if (this.guildPillPatch) return; const guildPill = WebpackModules.find(m => m.default && !m.default.displayName && m.default.toString && m.default.toString().includes("translate3d")); if (!guildPill) return; this.guildPillPatch = Utils.monkeyPatch(guildPill, "default", {after: (data) => { const props = data.methodArguments[0]; if (props.unread) data.returnValue.props.className += " bd-unread"; if (props.selected) data.returnValue.props.className += " bd-selected"; if (props.hovered) data.returnValue.props.className += " bd-hovered"; return data.returnValue; }}); }; Core.prototype.patchGuildSeparator = function() { if (this.guildSeparatorPatch) return; const Guilds = WebpackModules.findByDisplayName("Guilds"); const guildComponents = WebpackModules.findByProps("renderListItem"); if (!guildComponents || !Guilds) return; const GuildSeparator = function() { const returnValue = guildComponents.Separator(...arguments); returnValue.props.className += " bd-guild-separator"; return returnValue; }; this.guildSeparatorPatch = Utils.monkeyPatch(Guilds.prototype, "render", {after: (data) => { data.returnValue.props.children[1].props.children[3].type = GuildSeparator; }}); }; Core.prototype.patchAttachment = function() { if (this.AttachmentPatch) return; const Attachment = BDModules.get(e => e.default && e.default.displayName === "Attachment")[0] // temporary const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); if (!Anchor || !Attachment || !Attachment.default) return; this.AttachmentPatch = Utils.monkeyPatch(Attachment, "default", {after: (data) => { const attachment = data.methodArguments[0] || null const children = Utils.getNestedProp(data.returnValue, "props.children"); if (!children || !attachment || !attachment.url)return if (!Array.isArray(children)) return; const id = uuidv4() children.push(BDV2.react.createElement("div", { id: "certified-"+id })) PluginCertifier.patch(attachment, "certified-"+id) }}) } Core.prototype.patchMessageHeader = function() { if (this.messageHeaderPatch) return; const MessageHeader = WebpackModules.findByProps("MessageTimestamp"); const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); if (!Anchor || !MessageHeader || !MessageHeader.default) return; this.messageHeaderPatch = Utils.monkeyPatch(MessageHeader, "default", {after: (data) => { const author = Utils.getNestedProp(data.methodArguments[0], "message.author"); // const header = Utils.getNestedProp(data.returnValue, "props.children.1.props"); const children = Utils.getNestedProp(data.returnValue, "props.children.1.props.children.1.props.children"); if (!children || !author || !author.id)return // if (header && header.className) header.className += " " if (!Array.isArray(children)) return; if (author.id === "249746236008169473") { // Rauenzi: BandagedBD Developer children.push( BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD Developer"}, BDV2.React.createElement(Anchor, {className: "bd-chat-badge", href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-logo"}) ) ) ); } else if (author.id === "696481194443014174" || author.id === "696003456611385396"){ // Not Thomiz: Lightcord Developer, Phorcys: Lightcord Developer children.push( BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord Developer"}, BDV2.React.createElement(Anchor, {className: "bd-chat-badge", href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-logo"}) ) ) ); } const id = uuidv4() children.push( BDV2.React.createElement("div", { id: "badges-"+id, style: { display: "inline" } }) ) applyBadges(id, author, true) }}); }; function applyBadges(id, user, chat){ process.nextTick(() => { const div = document.getElementById("badges-"+id) if(!div || div.childNodes.length > 0)return if(div.childNodes.length)return let blockDiv = document.createElement("div") blockDiv.style.display = "none" div.appendChild(blockDiv) const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); distant.getBadges(user.id) .then(badges => { badges.forEach(badge => { const props = { svg: { size: "16px", className: "bd-logo", width: "16px", } } badge.scopes.forEach(scope => { if(scope === "user"){// require user props.user = user } }) if(!badge.href){ props.Anchor = Anchor props.href = { className: chat ? "bd-chat-badge" : "bd-member-badge", title: badge.name, target: "_blank" } } const element = BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: badge.name}, badge.href ? BDV2.react.createElement(Anchor, { href: badge.href, className: chat ? "bd-chat-badge" : "bd-member-badge", title: badge.name, target: "_blank" }, BDV2.React.createElement(badge.component, props)) : BDV2.React.createElement(badge.component, props) ) const div2 = document.createElement("div") BDV2.reactDom.render(element, div2) div2.childNodes.forEach(node => { div.appendChild(node) }) }) }) }) } Core.prototype.patchMemberList = function() { if (this.memberListPatch) return; const MemberListItem = WebpackModules.findByDisplayName("MemberListItem"); const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); if (!Anchor || !MemberListItem || !MemberListItem.prototype || !MemberListItem.prototype.renderDecorators) return; this.memberListPatch = Utils.monkeyPatch(MemberListItem.prototype, "renderDecorators", {after: (data) => { const user = Utils.getNestedProp(data.thisObject, "props.user"); const children = Utils.getNestedProp(data.returnValue, "props.children"); if (!children || !user || !user.id)return // if (header && header.className) header.className += " " if (!Array.isArray(children)) return; if (user.id === "249746236008169473") { children.push( BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD Developer"}, BDV2.React.createElement(Anchor, {className: "bd-member-badge", href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-logo"}) ) ) ); } else if (user.id === "696481194443014174" || user.id === "696003456611385396"){ children.push( BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord Developer"}, BDV2.React.createElement(Anchor, {className: "bd-member-badge", href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-logo"}) ) ) ); } const id = uuidv4() children.push( BDV2.React.createElement("div", {id: "badges-"+id}) ) applyBadges(id, user, false) }}); }; Core.prototype.updateInjector = async function() { const injectionPath = DataStore.injectionPath; if (!injectionPath) return false; const fs = require("fs"); const path = require("path"); const rmrf = require("rimraf"); const yauzl = require("yauzl"); const mkdirp = require("mkdirp"); const request = /*require("request");*/ null const parentPath = path.resolve(injectionPath, ".."); const folderName = path.basename(injectionPath); const zipLink = "https://github.com/rauenzi/BetterDiscordApp/archive/injector.zip"; const savedZip = path.resolve(parentPath, "injector.zip"); const extractedFolder = path.resolve(parentPath, "BetterDiscordApp-injector"); // Download the injector zip file Utils.log("InjectorUpdate", "Downloading " + zipLink); let success = await new Promise(resolve => { request.get({url: zipLink, encoding: null}, async (error, response, body) => { if (error || response.statusCode !== 200) return resolve(false); // Save a backup in case someone has their own copy const alreadyExists = await new Promise(res => fs.exists(savedZip, res)); if (alreadyExists) await new Promise(res => fs.rename(savedZip, `${savedZip}.bak${Math.round(performance.now())}`, res)); Utils.log("InjectorUpdate", "Writing " + savedZip); fs.writeFile(savedZip, body, err => resolve(!err)); }); }); if (!success) return success; // Check and delete rename extraction const alreadyExists = await new Promise(res => fs.exists(extractedFolder, res)); if (alreadyExists) await new Promise(res => fs.rename(extractedFolder, `${extractedFolder}.bak${Math.round(performance.now())}`, res)); // Unzip the downloaded zip file const zipfile = await new Promise(r => yauzl.open(savedZip, {lazyEntries: true}, (err, zip) => r(zip))); zipfile.on("entry", function(entry) { // Skip directories, they are handled with mkdirp if (entry.fileName.endsWith("/")) return zipfile.readEntry(); Utils.log("InjectorUpdate", "Extracting " + entry.fileName); // Make any needed parent directories const fullPath = path.resolve(parentPath, entry.fileName); mkdirp.sync(path.dirname(fullPath)); zipfile.openReadStream(entry, function(err, readStream) { if (err) return success = false; readStream.on("end", function() {zipfile.readEntry();}); // Go to next file after this readStream.pipe(fs.createWriteStream(fullPath)); }); }); zipfile.readEntry(); // Start reading // Wait for the final file to finish await new Promise(resolve => zipfile.once("end", resolve)); // Save a backup in case something goes wrong during final step const backupFolder = path.resolve(parentPath, `${folderName}.bak${Math.round(performance.now())}`); await new Promise(resolve => fs.rename(injectionPath, backupFolder, resolve)); // Rename the extracted folder to what it should be Utils.log("InjectorUpdate", `Renaming ${path.basename(extractedFolder)} to ${folderName}`); success = await new Promise(resolve => fs.rename(extractedFolder, injectionPath, err => resolve(!err))); if (!success) { Utils.err("InjectorUpdate", "Failed to rename the final directory"); return success; } // If rename had issues, delete what we tried to rename and restore backup if (!success) { Utils.err("InjectorUpdate", "Something went wrong... restoring backups."); await new Promise(resolve => rmrf(extractedFolder, resolve)); await new Promise(resolve => fs.rename(backupFolder, injectionPath, resolve)); return success; } // If we've gotten to this point, everything should have gone smoothly. // Cleanup the backup folder then remove the zip await new Promise(resolve => rmrf(backupFolder, resolve)); await new Promise(resolve => fs.unlink(savedZip, resolve)); Utils.log("InjectorUpdate", "Injector Updated!"); return success; }; export default new Core();