Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Mopsgamer 2022-12-13 18:08:13 +01:00
commit 5cd430c41a
9 changed files with 170 additions and 121 deletions

View File

@ -2,6 +2,37 @@
This changelog starts with the restructured 1.0.0 release that happened after context isolation changes. The changelogs here should more-or-less mirror the ones that get shown in the client but probably with less formatting and pizzazz.
## 1.8.3
### Added
- Checking for old installs and deleting them
### Removed
- All references to Emotes, this will become a separate plugin
### Changed
- Moved to the more permissive Apache 2.0 license
- Now check for discord.asar for electron17+
- Handle setting module exports internally rather than maintaining getter references
### Fixed
- Fixed `inject` for electron17+
- Updater checking `>` which does not work for open versions
- Fixed a startup bug with the context menu api
## 1.8.2
### Added
### Removed
### Changed
### Fixed
- Fixed modals not working
- Fixed downloading binary files
- Fixed public server invites
## 1.8.1
### Added

View File

@ -1,7 +1,15 @@
import fs from "fs";
import path from "path";
import {app} from "electron";
import Module from "module";
import path from "path";
import fs from "fs";
// Detect old install and delete it
const appPath = app.getAppPath(); // Should point to app or app.asar
const oldInstall = path.resolve(appPath, "..", "app");
if (fs.existsSync(oldInstall)) {
fs.rmdirSync(oldInstall, {recursive: true});
app.quit();
app.relaunch();
}
import ipc from "./modules/ipc";
import BrowserWindow from "./modules/browserwindow";
@ -19,32 +27,30 @@ if (!process.argv.includes("--vanilla")) {
// Remove CSP immediately on linux since they install to discord_desktop_core still
if (process.platform == "win32" || process.platform == "darwin") app.once("ready", CSP.remove);
else CSP.remove();
try {
CSP.remove();
}
catch (_) {
// Remove when everyone is moved to core
}
}
// Enable DevTools on Stable.
let fakeAppSettings;
Object.defineProperty(global, "appSettings", {
get() {
return fakeAppSettings;
},
set(value) {
if (!value.hasOwnProperty("settings")) value.settings = {};
value.settings.DANGEROUS_ENABLE_DEVTOOLS_ONLY_ENABLE_IF_YOU_KNOW_WHAT_YOURE_DOING = true;
fakeAppSettings = value;
},
});
// Use Discord's info to run the app
if (process.platform == "win32" || process.platform == "darwin") {
const appAsar = path.join(app.getAppPath(), "..", "app.asar");
const discordAsar = path.join(app.getAppPath(), "..", "discord.asar");
const basePath = fs.existsSync(discordAsar) ? discordAsar : appAsar;
const pkg = __non_webpack_require__(path.join(basePath, "package.json"));
app.setAppPath(basePath);
app.name = pkg.name;
Module._load(path.join(basePath, pkg.main), null, true);
try {
let fakeAppSettings;
Object.defineProperty(global, "appSettings", {
get() {
return fakeAppSettings;
},
set(value) {
if (!value.hasOwnProperty("settings")) value.settings = {};
value.settings.DANGEROUS_ENABLE_DEVTOOLS_ONLY_ENABLE_IF_YOU_KNOW_WHAT_YOURE_DOING = true;
fakeAppSettings = value;
},
});
}
catch (_) {
// Remove when everyone is moved to core
}
// Needs to run this after Discord but before ready()

View File

@ -46,16 +46,6 @@ export default class BetterDiscord {
if (!fs.existsSync(path.join(dataPath, "themes"))) fs.mkdirSync(path.join(dataPath, "themes"));
}
static async ensureWebpackModules(browserWindow) {
await browserWindow.webContents.executeJavaScript(`new Promise(resolve => {
const check = function() {
if (window.webpackJsonp && window.webpackJsonp.flat().flat().length >= 7000) return resolve();
setTimeout(check, 100);
};
check();
});`);
}
static async injectRenderer(browserWindow) {
const location = path.join(__dirname, "renderer.js");
if (!fs.existsSync(location)) return; // TODO: cut a fatal log

View File

@ -128,17 +128,23 @@ const registerPreload = (event, path) => {
export default class IPCMain {
static registerEvents() {
ipc.on(IPCEvents.GET_PATH, getPath);
ipc.on(IPCEvents.RELAUNCH, relaunch);
ipc.on(IPCEvents.OPEN_DEVTOOLS, openDevTools);
ipc.on(IPCEvents.CLOSE_DEVTOOLS, closeDevTools);
ipc.on(IPCEvents.TOGGLE_DEVTOOLS, toggleDevTools);
ipc.on(IPCEvents.INSPECT_ELEMENT, inspectElement);
ipc.on(IPCEvents.MINIMUM_SIZE, setMinimumSize);
ipc.on(IPCEvents.DEVTOOLS_WARNING, stopDevtoolsWarning);
ipc.on(IPCEvents.REGISTER_PRELOAD, registerPreload);
ipc.handle(IPCEvents.RUN_SCRIPT, runScript);
ipc.handle(IPCEvents.OPEN_DIALOG, openDialog);
ipc.handle(IPCEvents.OPEN_WINDOW, createBrowserWindow);
try {
ipc.on(IPCEvents.GET_PATH, getPath);
ipc.on(IPCEvents.RELAUNCH, relaunch);
ipc.on(IPCEvents.OPEN_DEVTOOLS, openDevTools);
ipc.on(IPCEvents.CLOSE_DEVTOOLS, closeDevTools);
ipc.on(IPCEvents.TOGGLE_DEVTOOLS, toggleDevTools);
ipc.on(IPCEvents.INSPECT_ELEMENT, inspectElement);
ipc.on(IPCEvents.MINIMUM_SIZE, setMinimumSize);
ipc.on(IPCEvents.DEVTOOLS_WARNING, stopDevtoolsWarning);
ipc.on(IPCEvents.REGISTER_PRELOAD, registerPreload);
ipc.handle(IPCEvents.RUN_SCRIPT, runScript);
ipc.handle(IPCEvents.OPEN_DIALOG, openDialog);
ipc.handle(IPCEvents.OPEN_WINDOW, createBrowserWindow);
}
catch (err) {
// eslint-disable-next-line no-console
console.error(err);
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "betterdiscord",
"version": "1.8.2",
"version": "1.8.3",
"description": "Enhances Discord by adding functionality and themes.",
"main": "src/index.js",
"scripts": {

View File

@ -2,15 +2,21 @@
export default {
description: "Just some smaller fixes while we work on some big things in the background.",
changes: [
{
title: "What's New?",
type: "",
items: [
"Twitch emotes are no longer a part of BetterDiscord, we are opting to move them to a plugin. Stay tuned for when that will be completed!",
"We changed the way patching is handled internally hoping it'll help with memory leaks.",
"Everything should be working again for Canary and PTB with the latest updates!",
]
},
{
title: "Bug Fixes",
type: "fixed",
items: [
"Fixed _even more_ issues with the built-in updater.",
"Fixed not being able to click support server links in plugin/theme pages.",
"Fixed some issues with not being able to join public servers.",
"Fixed plugin settings not being able to be displayed.",
"Fixed changelog modal not being able to be displayed."
"Fixed the way the update compares version numbers.",
"Fixed the startup bug."
]
}
]

View File

@ -3,6 +3,8 @@ import Patcher from "../patcher";
import Logger from "common/logger";
import {React} from "../modules";
let startupComplete = false;
const MenuComponents = (() => {
const out = {};
const componentMap = {
@ -14,14 +16,25 @@ const MenuComponents = (() => {
customitem: "Item"
};
let ContextMenuIndex = null;
const ContextMenuModule = WebpackModules.getModule((m, _, id) => Object.values(m).some(v => v?.FLEXIBLE) && (ContextMenuIndex = id), {searchExports: false});
const rawMatches = WebpackModules.require.m[ContextMenuIndex].toString().matchAll(/if\(\w+\.type===\w+\.(\w+)\).+?type:"(.+?)"/g);
out.Menu = Object.values(ContextMenuModule).find(v => v.toString().includes(".isUsingKeyboardNavigation"));
try {
let contextMenuId = Object.keys(WebpackModules.require.m).find(e => WebpackModules.require.m[e]?.toString().includes("menuitemcheckbox"));
const ContextMenuModule = WebpackModules.getModule((m, t, id) => id === contextMenuId);
const rawMatches = WebpackModules.require.m[contextMenuId].toString().matchAll(/if\(\w+\.type===\w+\.(\w+)\).+?type:"(.+?)"/g);
for (const [, identifier, type] of rawMatches) {
out[componentMap[type]] = ContextMenuModule[identifier];
out.Menu = Object.values(ContextMenuModule).find(v => v.toString().includes(".isUsingKeyboardNavigation"));
for (const [, identifier, type] of rawMatches) {
out[componentMap[type]] = ContextMenuModule[identifier];
}
startupComplete = Object.values(componentMap).every(k => out[k]) && !!out.Menu;
} catch (error) {
startupComplete = false;
Logger.stacktrace("ContextMenu~Components", "Fatal startup error:", error);
Object.assign(out, Object.fromEntries(
Object.values(componentMap).map(k => [k, () => null])
));
}
return out;
@ -30,15 +43,27 @@ const MenuComponents = (() => {
const ContextMenuActions = (() => {
const out = {};
const ActionsModule = WebpackModules.getModule(m => Object.values(m).some(v => typeof v === "function" && v.toString().includes("CONTEXT_MENU_CLOSE")), {searchExports: false});
try {
const ActionsModule = WebpackModules.getModule(m => Object.values(m).some(v => typeof v === "function" && v.toString().includes("CONTEXT_MENU_CLOSE")), {searchExports: false});
for (const key of Object.keys(ActionsModule)) {
if (ActionsModule[key].toString().includes("CONTEXT_MENU_CLOSE")) {
out.closeContextMenu = ActionsModule[key];
}
else if (ActionsModule[key].toString().includes("renderLazy")) {
out.openContextMenu = ActionsModule[key];
for (const key of Object.keys(ActionsModule)) {
if (ActionsModule[key].toString().includes("CONTEXT_MENU_CLOSE")) {
out.closeContextMenu = ActionsModule[key];
}
else if (ActionsModule[key].toString().includes("renderLazy")) {
out.openContextMenu = ActionsModule[key];
}
}
startupComplete = typeof(out.closeContextMenu) === "function" && typeof(out.openContextMenu) === "function";
} catch (error) {
startupComplete = false;
Logger.stacktrace("ContextMenu~Components", "Fatal startup error:", error);
Object.assign(out, {
closeContextMenu: () => {},
openContextMenu: () => {}
});
}
return out;
@ -50,6 +75,8 @@ class MenuPatcher {
static subPatches = new WeakMap();
static initialize() {
if (!startupComplete) return Logger.warn("ContextMenu~Patcher", "Startup wasn't successfully, aborting initialization.");
const {module, key} = (() => {
const foundModule = WebpackModules.getModule(m => Object.values(m).some(v => typeof v === "function" && v.toString().includes("CONTEXT_MENU_CLOSE")), {searchExports: false});
const foundKey = Object.keys(foundModule).find(k => foundModule[k].length === 3);
@ -320,6 +347,11 @@ class ContextMenu {
Object.assign(ContextMenu.prototype, MenuComponents);
Object.freeze(ContextMenu);
Object.freeze(ContextMenu.prototype);
MenuPatcher.initialize();
try {
MenuPatcher.initialize();
} catch (error) {
Logger.error("ContextMenu~Patcher", "Fatal error:", error);
}
export default ContextMenu;

View File

@ -134,6 +134,25 @@ export class CoreUpdater {
}
}
const semverRegex = /^[0-9]+\.[0-9]+\.[0-9]+$/;
/**
* This works on basic semantic versioning e.g. "1.0.0".
*
* @param {string} currentVersion
* @param {string} content
* @returns {boolean} whether there is an update
*/
function semverComparator(currentVersion, remoteVersion) {
currentVersion = currentVersion.split(".").map((e) => {return parseInt(e);});
remoteVersion = remoteVersion.split(".").map((e) => {return parseInt(e);});
if (remoteVersion[0] > currentVersion[0]) return true;
else if (remoteVersion[0] == currentVersion[0] && remoteVersion[1] > currentVersion[1]) return true;
else if (remoteVersion[0] == currentVersion[0] && remoteVersion[1] == currentVersion[1] && remoteVersion[2] > currentVersion[2]) return true;
return false;
}
class AddonUpdater {
@ -176,7 +195,10 @@ class AddonUpdater {
if (this.pending.includes(filename)) return;
const info = this.cache[path.basename(filename)];
if (!info) return;
const hasUpdate = info.version > currentVersion;
let hasUpdate = info.update > currentVersion;
if (semverRegex.test(info.version) && semverRegex.test(currentVersion)) {
hasUpdate = semverComparator(currentVersion, info.version);
}
if (!hasUpdate) return;
this.pending.push(filename);
}

View File

@ -15,18 +15,13 @@ const discordPath = (function() {
const basedir = path.join(process.env.LOCALAPPDATA, release.replace(/ /g, ""));
if (!fs.existsSync(basedir)) throw new Error(`Cannot find directory for ${release}`);
const version = fs.readdirSync(basedir).filter(f => fs.lstatSync(path.join(basedir, f)).isDirectory() && f.split(".").length > 1).sort().reverse()[0];
resourcePath = path.join(basedir, version, "resources");
}
else if (process.platform === "darwin") {
const appPath = releaseInput === "canary" ? path.join("/Applications", "Discord Canary.app")
: releaseInput === "ptb" ? path.join("/Applications", "Discord PTB.app")
: useBdRelease && args[3] ? args[3] ? args[2] : args[2]
: path.join("/Applications", "Discord.app");
resourcePath = path.join(appPath, "Contents", "Resources");
// To account for discord_desktop_core-1 or any other number
const coreWrap = fs.readdirSync(path.join(basedir, version, "modules")).filter(e => e.indexOf("discord_desktop_core") === 0).sort().reverse()[0];
resourcePath = path.join(basedir, version, "modules", coreWrap, "discord_desktop_core");
}
else {
const userData = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : path.join(process.env.HOME, ".config");
let userData = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : path.join(process.env.HOME, ".config");
if (process.platform === "darwin") userData = path.join(process.env.HOME, "Library", "Application Support");
const basedir = path.join(userData, release.toLowerCase().replace(" ", ""));
if (!fs.existsSync(basedir)) return "";
const version = fs.readdirSync(basedir).filter(f => fs.lstatSync(path.join(basedir, f)).isDirectory() && f.split(".").length > 1).sort().reverse()[0];
@ -46,50 +41,11 @@ console.log(`Injecting into ${release}`);
if (!fs.existsSync(discordPath)) throw new Error(`Cannot find directory for ${release}`);
console.log(` ✅ Found ${release} in ${discordPath}`);
const appPath = process.platform === "win32" || process.platform === "darwin" ? path.join(discordPath, "app") : discordPath;
const packageJson = path.join(appPath, "package.json");
const indexJs = path.join(appPath, "index.js");
if (!fs.existsSync(appPath)) fs.mkdirSync(appPath);
if (fs.existsSync(packageJson)) fs.unlinkSync(packageJson);
const indexJs = path.join(discordPath, "index.js");
if (fs.existsSync(indexJs)) fs.unlinkSync(indexJs);
if (process.platform === "win32" || process.platform === "darwin") {
fs.writeFileSync(packageJson, JSON.stringify({
name: "betterdiscord",
main: "index.js",
}, null, 4));
console.log(" ✅ Wrote package.json");
fs.writeFileSync(indexJs, `require("${bdPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}");`);
}
else {
fs.writeFileSync(indexJs, `require("${bdPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}");\nmodule.exports = require("./core.asar");`);
}
fs.writeFileSync(indexJs, `require("${bdPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}");\nmodule.exports = require("./core.asar");`);
console.log(" ✅ Wrote index.js");
console.log("");
const asarPath = path.join(discordPath, "app.asar");
const modifiedPath = path.join(discordPath, "discord.asar");
if (!fs.existsSync(modifiedPath)) {
console.log("Renaming app.asar -> discord.asar");
console.log("");
if (!fs.existsSync(asarPath)) {
console.log(" ❌ Unable to rename app.asar -> discord.asar, discord installation appears to be corrupt.");
process.exit(0);
}
try {
fs.renameSync(asarPath, modifiedPath);
console.log(" ✅ Successfully renamed app.asar -> discord.asar");
console.log("");
} catch (error) {
console.log(" ❌ Failed to rename app.asar -> discord.asar:", error);
process.exit(0);
}
}
console.log(`Injection successful, please restart ${release}.`);