From 2ac01b9fb7d418cde5d2b41bf143417732dc76fc Mon Sep 17 00:00:00 2001 From: LoneWeeb <73281112+Tharki-God@users.noreply.github.com> Date: Sun, 16 Jul 2023 15:35:30 +0530 Subject: [PATCH 01/17] added types --- scripts/inject/types.mts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/inject/types.mts b/scripts/inject/types.mts index 810797c12..a50714808 100644 --- a/scripts/inject/types.mts +++ b/scripts/inject/types.mts @@ -5,3 +5,8 @@ export type DiscordPlatform = "stable" | "ptb" | "canary" | "dev"; export interface PlatformModule { getAppDir: (platform: DiscordPlatform) => Promisable; } + +export interface ProcessInfo { + pid: number; + cmd: string[]; +} From edba415621f010bf17caaab0043f6982c9e4a91d Mon Sep 17 00:00:00 2001 From: LoneWeeb <73281112+Tharki-God@users.noreply.github.com> Date: Sun, 16 Jul 2023 15:37:32 +0530 Subject: [PATCH 02/17] added util functions --- scripts/inject/util.mts | 56 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/scripts/inject/util.mts b/scripts/inject/util.mts index c7d8fa13c..8a19171c6 100644 --- a/scripts/inject/util.mts +++ b/scripts/inject/util.mts @@ -1,4 +1,5 @@ -import { DiscordPlatform } from "./types.mjs"; +import { SpawnOptions, execSync, spawn } from "child_process"; +import { DiscordPlatform, ProcessInfo } from "./types.mjs"; export const AnsiEscapes = { RESET: "\x1b[0m", @@ -29,3 +30,56 @@ export const getCommand = ({ cmd += ` ${platform || `[${Object.keys(PlatformNames).join("|")}]`}`; return cmd; }; + +export const getProcessInfoByName = (processName: string): ProcessInfo | null => { + if (process.platform === "win32") { + const command = `tasklist /FI "IMAGENAME eq ${processName}.exe" /FO CSV`; + const output = execSync(command).toString().trim(); + + const lines = output.split("\r\n"); + if (lines.length <= 2) { + return null; + } + + const [_header, data] = lines.slice(0, 2); + const [name, pid] = data.split('","'); + + return { pid: Number(pid), cmd: name.substring(1).split(/\s+/) }; + } + const command = `ps -eo pid,command | grep -E "(^|/)${processName}(\\s|$)" | grep -v grep`; + const output = execSync(command).toString().trim(); + + if (output.length === 0) { + return null; + } + + const [pid, ...cmd] = output.split(/\s+/); + + return { pid: Number(pid), cmd }; +}; + +export const killCheckProcessExists = (pid: number): boolean => { + try { + process.kill(pid, 0); + return true; + } catch { + return false; + } +}; + +export const killProcessByPID = (pid: number): Promise => { + return new Promise((resolve) => { + if (!pid) resolve(); + process.kill(pid, "SIGTERM"); + const checkInterval = setInterval(() => { + if (!killCheckProcessExists(pid)) { + clearInterval(checkInterval); + resolve(); + } + }, 1000); + }); +}; + +export const openProcess = (command: string, args: string[], options: SpawnOptions): void => { + spawn(command, args, options).unref(); +}; From ae7daf7ed4efa0265b0362b2687b8de0478124b9 Mon Sep 17 00:00:00 2001 From: LoneWeeb <73281112+Tharki-God@users.noreply.github.com> Date: Sun, 16 Jul 2023 15:38:02 +0530 Subject: [PATCH 03/17] main changes in script --- scripts/inject/index.mts | 42 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/scripts/inject/index.mts b/scripts/inject/index.mts index da317f92b..9441fb0b7 100644 --- a/scripts/inject/index.mts +++ b/scripts/inject/index.mts @@ -3,7 +3,14 @@ import "./checks/elevate.mjs"; import "./checks/env.mjs"; import { join } from "path"; -import { AnsiEscapes, getCommand } from "./util.mjs"; +import { + AnsiEscapes, + PlatformNames, + getCommand, + getProcessInfoByName, + killProcessByPID, + openProcess, +} from "./util.mjs"; import { inject, uninject } from "./injector.mjs"; import * as darwin from "./platforms/darwin.mjs"; @@ -20,6 +27,7 @@ const platformModules = { const exitCode = process.argv.includes("--no-exit-codes") ? 0 : 1; const prod = process.argv.includes("--production"); +const noRelaunch = process.argv.includes("--no-relaunch"); const processArgs = process.argv.filter((v) => !v.startsWith("-")); if (!(process.platform in platformModules)) { @@ -94,7 +102,20 @@ const run = async (cmd = processArgs[2], replug = false): Promise => { if (cmd === "inject") { try { - result = await inject(platformModule, platform, prod); + if (noRelaunch) { + result = await inject(platformModule, platform, prod); + } else { + const processName = PlatformNames[platform].replace(" ", ""); + const processInfo = getProcessInfoByName(processName)!; + await killProcessByPID(processInfo?.pid); + result = await inject(platformModule, platform, prod); + const appDir = await platformModule.getAppDir(platform); + openProcess( + join(appDir, "..", "..", "..", "Update"), + process.platform === "win32" ? ["--processStart", `${processName}.exe`] : [], + { detached: true, stdio: "ignore" }, + ); + } } catch (e) { console.error( `${AnsiEscapes.RED}An error occurred while trying to inject into Discord!${AnsiEscapes.RESET}`, @@ -121,7 +142,22 @@ To plug into a different platform, use the following syntax: ${AnsiEscapes.BOLD} } } else if (cmd === "uninject") { try { - result = await uninject(platformModule, platform); + if (noRelaunch) { + result = await uninject(platformModule, platform); + } else { + const processName = PlatformNames[platform].replace(" ", ""); + const processInfo = getProcessInfoByName(processName)!; + await killProcessByPID(processInfo?.pid); + result = await uninject(platformModule, platform); + if (!replug) { + const appDir = await platformModule.getAppDir(platform); + openProcess( + join(appDir, "..", "..", "..", "Update"), + process.platform === "win32" ? ["--processStart", "Discord.exe"] : [], + { detached: true, stdio: "ignore" }, + ); + } + } } catch (e) { console.error( `${AnsiEscapes.RED}An error occurred while trying to uninject from Discord!${AnsiEscapes.RESET}`, From a93391efac7bba53415892297eaac835fbd85b2a Mon Sep 17 00:00:00 2001 From: LoneWeeb <73281112+Tharki-God@users.noreply.github.com> Date: Sun, 16 Jul 2023 15:42:47 +0530 Subject: [PATCH 04/17] cspell fix --- cspell.json | 1 + 1 file changed, 1 insertion(+) diff --git a/cspell.json b/cspell.json index d07002b60..4092e19f6 100644 --- a/cspell.json +++ b/cspell.json @@ -30,6 +30,7 @@ "globstar", "groupstart", "HighlightJS", + "IMAGENAME", "installdir", "Jsonifiable", "konami", From c510c93dda4bcc060d532185ce84307d65cb7e69 Mon Sep 17 00:00:00 2001 From: LoneWeeb <73281112+Tharki-God@users.noreply.github.com> Date: Mon, 17 Jul 2023 04:15:47 +0530 Subject: [PATCH 05/17] timeout --- scripts/inject/util.mts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/inject/util.mts b/scripts/inject/util.mts index 8a19171c6..eea818f3f 100644 --- a/scripts/inject/util.mts +++ b/scripts/inject/util.mts @@ -77,6 +77,10 @@ export const killProcessByPID = (pid: number): Promise => { resolve(); } }, 1000); + setTimeout(() => { + clearInterval(checkInterval); + resolve(); + }, 6000); }); }; From 1835ddf9024d5466618627718b4db4d588c476fd Mon Sep 17 00:00:00 2001 From: LoneWeeb <73281112+Tharki-God@users.noreply.github.com> Date: Mon, 17 Jul 2023 04:16:18 +0530 Subject: [PATCH 06/17] added the function to injector --- scripts/inject/injector.mts | 59 ++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/scripts/inject/injector.mts b/scripts/inject/injector.mts index 2b4bd827c..fd73f6eb9 100644 --- a/scripts/inject/injector.mts +++ b/scripts/inject/injector.mts @@ -1,7 +1,14 @@ import { chown, copyFile, mkdir, rename, rm, stat, writeFile } from "fs/promises"; import path, { join, sep } from "path"; import { fileURLToPath } from "url"; -import { AnsiEscapes, getCommand } from "./util.mjs"; +import { + AnsiEscapes, + PlatformNames, + getCommand, + getProcessInfoByName, + killProcessByPID, + openProcess, +} from "./util.mjs"; import readline from "readline"; import { exec } from "child_process"; import { DiscordPlatform, PlatformModule } from "./types.mjs"; @@ -230,3 +237,53 @@ export const uninject = async ( await rename(join(appDir, "..", "app.orig.asar"), appDir); return true; }; + +export const smartInject = async ( + cmd: "uninject" | "inject", + replug: boolean, + platformModule: PlatformModule, + platform: DiscordPlatform, + production: boolean, + noRelaunch: boolean, +): Promise => { + let result; + if (noRelaunch) { + result = + cmd === "uninject" + ? await uninject(platformModule, platform) + : inject(platformModule, platform, production); + } else { + const processName = PlatformNames[platform].replace(" ", ""); + const processInfo = getProcessInfoByName(processName)!; + await killProcessByPID(processInfo?.pid); + result = + cmd === "uninject" + ? await uninject(platformModule, platform) + : inject(platformModule, platform, production); + if ((replug && cmd === "inject") || !replug) { + const appDir = await platformModule.getAppDir(platform); + switch (process.platform) { + case "win32": + openProcess( + join(appDir, "..", "..", "..", "Update"), + ["--processStart", `${processName}.exe`], + { detached: true, stdio: "ignore" }, + ); + break; + case "linux": + openProcess(join(appDir, "..", "..", processName), [], { + detached: true, + stdio: "ignore", + }); + break; + case "darwin": + openProcess(join(appDir, "..", "..", "MacOS", processName), [], { + detached: true, + stdio: "ignore", + }); + break; + } + } + } + return result; +}; From 8d033837cf30a0fc84c2f90f2a581f1852bc86fc Mon Sep 17 00:00:00 2001 From: LoneWeeb <73281112+Tharki-God@users.noreply.github.com> Date: Mon, 17 Jul 2023 04:16:58 +0530 Subject: [PATCH 07/17] use new function --- scripts/inject/index.mts | 43 ++++------------------------------------ 1 file changed, 4 insertions(+), 39 deletions(-) diff --git a/scripts/inject/index.mts b/scripts/inject/index.mts index 9441fb0b7..b27ed3a6d 100644 --- a/scripts/inject/index.mts +++ b/scripts/inject/index.mts @@ -3,15 +3,8 @@ import "./checks/elevate.mjs"; import "./checks/env.mjs"; import { join } from "path"; -import { - AnsiEscapes, - PlatformNames, - getCommand, - getProcessInfoByName, - killProcessByPID, - openProcess, -} from "./util.mjs"; -import { inject, uninject } from "./injector.mjs"; +import { AnsiEscapes, getCommand } from "./util.mjs"; +import { smartInject } from "./injector.mjs"; import * as darwin from "./platforms/darwin.mjs"; import * as linux from "./platforms/linux.mjs"; @@ -102,20 +95,7 @@ const run = async (cmd = processArgs[2], replug = false): Promise => { if (cmd === "inject") { try { - if (noRelaunch) { - result = await inject(platformModule, platform, prod); - } else { - const processName = PlatformNames[platform].replace(" ", ""); - const processInfo = getProcessInfoByName(processName)!; - await killProcessByPID(processInfo?.pid); - result = await inject(platformModule, platform, prod); - const appDir = await platformModule.getAppDir(platform); - openProcess( - join(appDir, "..", "..", "..", "Update"), - process.platform === "win32" ? ["--processStart", `${processName}.exe`] : [], - { detached: true, stdio: "ignore" }, - ); - } + result = await smartInject(cmd, replug, platformModule, platform, prod, noRelaunch); } catch (e) { console.error( `${AnsiEscapes.RED}An error occurred while trying to inject into Discord!${AnsiEscapes.RESET}`, @@ -142,22 +122,7 @@ To plug into a different platform, use the following syntax: ${AnsiEscapes.BOLD} } } else if (cmd === "uninject") { try { - if (noRelaunch) { - result = await uninject(platformModule, platform); - } else { - const processName = PlatformNames[platform].replace(" ", ""); - const processInfo = getProcessInfoByName(processName)!; - await killProcessByPID(processInfo?.pid); - result = await uninject(platformModule, platform); - if (!replug) { - const appDir = await platformModule.getAppDir(platform); - openProcess( - join(appDir, "..", "..", "..", "Update"), - process.platform === "win32" ? ["--processStart", "Discord.exe"] : [], - { detached: true, stdio: "ignore" }, - ); - } - } + result = await smartInject(cmd, replug, platformModule, platform, prod, noRelaunch); } catch (e) { console.error( `${AnsiEscapes.RED}An error occurred while trying to uninject from Discord!${AnsiEscapes.RESET}`, From fb64269af9b0f8508d20bccccb4f3f8cb854b25a Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Wed, 19 Jul 2023 04:28:11 +0530 Subject: [PATCH 08/17] linux support added??? --- scripts/inject/injector.mts | 10 ++++++++-- scripts/inject/types.mts | 6 ++++++ scripts/inject/util.mts | 11 ++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/scripts/inject/injector.mts b/scripts/inject/injector.mts index fd73f6eb9..b850cf8e9 100644 --- a/scripts/inject/injector.mts +++ b/scripts/inject/injector.mts @@ -3,6 +3,7 @@ import path, { join, sep } from "path"; import { fileURLToPath } from "url"; import { AnsiEscapes, + GetUserData, PlatformNames, getCommand, getProcessInfoByName, @@ -254,8 +255,12 @@ export const smartInject = async ( : inject(platformModule, platform, production); } else { const processName = PlatformNames[platform].replace(" ", ""); - const processInfo = getProcessInfoByName(processName)!; - await killProcessByPID(processInfo?.pid); + try { + if ((replug && cmd === "uninject") || !replug){ + const processInfo = getProcessInfoByName(processName)!; + await killProcessByPID(processInfo?.pid); + } + } catch {} result = cmd === "uninject" ? await uninject(platformModule, platform) @@ -272,6 +277,7 @@ export const smartInject = async ( break; case "linux": openProcess(join(appDir, "..", "..", processName), [], { + ...GetUserData(), detached: true, stdio: "ignore", }); diff --git a/scripts/inject/types.mts b/scripts/inject/types.mts index a50714808..e878668b1 100644 --- a/scripts/inject/types.mts +++ b/scripts/inject/types.mts @@ -10,3 +10,9 @@ export interface ProcessInfo { pid: number; cmd: string[]; } + +export interface UserData { + env: NodeJS.ProcessEnv; + uid: number; + gid: number; +} diff --git a/scripts/inject/util.mts b/scripts/inject/util.mts index eea818f3f..22e48ec7f 100644 --- a/scripts/inject/util.mts +++ b/scripts/inject/util.mts @@ -1,5 +1,5 @@ import { SpawnOptions, execSync, spawn } from "child_process"; -import { DiscordPlatform, ProcessInfo } from "./types.mjs"; +import { DiscordPlatform, ProcessInfo, UserData } from "./types.mjs"; export const AnsiEscapes = { RESET: "\x1b[0m", @@ -47,6 +47,7 @@ export const getProcessInfoByName = (processName: string): ProcessInfo | null => return { pid: Number(pid), cmd: name.substring(1).split(/\s+/) }; } const command = `ps -eo pid,command | grep -E "(^|/)${processName}(\\s|$)" | grep -v grep`; + const output = execSync(command).toString().trim(); if (output.length === 0) { @@ -87,3 +88,11 @@ export const killProcessByPID = (pid: number): Promise => { export const openProcess = (command: string, args: string[], options: SpawnOptions): void => { spawn(command, args, options).unref(); }; + +export const GetUserData = (): UserData => { + const name = execSync("logname", { encoding: "utf8" }).toString().trim().replace(/\n$/, ""); + const env = Object.assign({}, process.env, { HOME: `/home/${name}` }); + const uid = execSync(`id -u ${name}`, { encoding: "utf8" }).toString().trim().replace(/\n$/, ""); + const gid = execSync(`id -g ${name}`, { encoding: "utf8" }).toString().trim().replace(/\n$/, ""); + return { env, uid: Number(uid), gid: Number(gid) }; +}; From aa557353df758c5f77b478dd04a48af8583820d6 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Wed, 19 Jul 2023 04:30:50 +0530 Subject: [PATCH 09/17] prettier is *** --- scripts/inject/injector.mts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/inject/injector.mts b/scripts/inject/injector.mts index b850cf8e9..6f630c293 100644 --- a/scripts/inject/injector.mts +++ b/scripts/inject/injector.mts @@ -256,11 +256,11 @@ export const smartInject = async ( } else { const processName = PlatformNames[platform].replace(" ", ""); try { - if ((replug && cmd === "uninject") || !replug){ + if ((replug && cmd === "uninject") || !replug) { const processInfo = getProcessInfoByName(processName)!; await killProcessByPID(processInfo?.pid); - } - } catch {} + } + } catch {} result = cmd === "uninject" ? await uninject(platformModule, platform) From 5c2ddf4f6c6eac36e5669b6b75fc9476d4e48d25 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Sun, 24 Sep 2023 11:56:48 +0530 Subject: [PATCH 10/17] requested changes --- scripts/inject/injector.mts | 9 +++------ scripts/inject/util.mts | 6 ++++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts/inject/injector.mts b/scripts/inject/injector.mts index 13853487d..b9906ab28 100644 --- a/scripts/inject/injector.mts +++ b/scripts/inject/injector.mts @@ -270,9 +270,9 @@ export const smartInject = async ( : inject(platformModule, platform, production); } else { const processName = PlatformNames[platform].replace(" ", ""); + const processInfo = getProcessInfoByName(processName)!; try { if ((replug && cmd === "uninject") || !replug) { - const processInfo = getProcessInfoByName(processName)!; await killProcessByPID(processInfo?.pid); } } catch {} @@ -280,7 +280,7 @@ export const smartInject = async ( cmd === "uninject" ? await uninject(platformModule, platform) : inject(platformModule, platform, production); - if ((replug && cmd === "inject") || !replug) { + if (((replug && cmd === "inject") || !replug) && processInfo) { const appDir = await platformModule.getAppDir(platform); switch (process.platform) { case "win32": @@ -298,10 +298,7 @@ export const smartInject = async ( }); break; case "darwin": - openProcess(join(appDir, "..", "..", "MacOS", processName), [], { - detached: true, - stdio: "ignore", - }); + openProcess(`open -a ${PlatformNames[platform]}`); break; } } diff --git a/scripts/inject/util.mts b/scripts/inject/util.mts index 22e48ec7f..2856e975d 100644 --- a/scripts/inject/util.mts +++ b/scripts/inject/util.mts @@ -85,8 +85,10 @@ export const killProcessByPID = (pid: number): Promise => { }); }; -export const openProcess = (command: string, args: string[], options: SpawnOptions): void => { - spawn(command, args, options).unref(); +export const openProcess = (command: string, args?: string[], options?: SpawnOptions): void => { + void (process.platform === "darwin" + ? execSync(command) + : spawn(command, args ?? [], options ?? {}).unref()); }; export const GetUserData = (): UserData => { From e2b2613347615252b6d2b6c8bb5106c0a52fc3b3 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Thu, 19 Oct 2023 11:33:23 +0530 Subject: [PATCH 11/17] warnings --- scripts/inject/injector.mts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/inject/injector.mts b/scripts/inject/injector.mts index 7ad68cd61..3c952175e 100644 --- a/scripts/inject/injector.mts +++ b/scripts/inject/injector.mts @@ -10,13 +10,13 @@ import { killProcessByPID, openProcess, } from "./util.mjs"; -import readline from "readline"; -import { exec, execSync } from "child_process"; -import { DiscordPlatform, PlatformModule } from "./types.mjs"; +import { execSync } from "child_process"; +import { DiscordPlatform, PlatformModule, ProcessInfo } from "./types.mjs"; import { CONFIG_PATH } from "../../src/util.mjs"; import { existsSync } from "fs"; const dirname = path.dirname(fileURLToPath(import.meta.url)); +let processInfo: ProcessInfo; export const isDiscordInstalled = async (appDir: string, silent?: boolean): Promise => { try { @@ -100,7 +100,7 @@ export const inject = async ( return false; } - const fileToCheck = join(dirname, "..", "..", prod ? "replugged.asar" : "dist/main.js"); + const fileToCheck = join(dirname, "..", "..", prod ? "replugged.asar" : "dist-bundle/main.js"); const fileToCheckExists = await stat(fileToCheck) .then(() => true) .catch(() => false); @@ -119,7 +119,7 @@ export const inject = async ( const entryPoint = prod ? join(CONFIG_PATH, "replugged.asar") - : join(dirname, "..", "..", "dist/main.js"); + : join(dirname, "..", "..", "dist-bundle/main.js"); const entryPointDir = path.dirname(entryPoint); if (appDir.includes("flatpak")) { @@ -227,9 +227,9 @@ export const smartInject = async ( : inject(platformModule, platform, production); } else { const processName = PlatformNames[platform].replace(" ", ""); - const processInfo = getProcessInfoByName(processName)!; try { if ((replug && cmd === "uninject") || !replug) { + processInfo = getProcessInfoByName(processName)!; await killProcessByPID(processInfo?.pid); } } catch {} @@ -237,7 +237,7 @@ export const smartInject = async ( cmd === "uninject" ? await uninject(platformModule, platform) : inject(platformModule, platform, production); - if (((replug && cmd === "inject") || !replug) && processInfo) { + if (((replug && cmd !== "uninject") || !replug) && processInfo) { const appDir = await platformModule.getAppDir(platform); switch (process.platform) { case "win32": From 365c37faac22a64892b4dce56aa8817bc76410b3 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Fri, 12 Jul 2024 01:43:01 +0530 Subject: [PATCH 12/17] prettier and dev mode fix --- scripts/inject/injector.mts | 2 +- src/renderer/apis/settings.ts | 4 ++-- src/renderer/coremods/commands/commands.ts | 8 ++++---- src/renderer/coremods/settings/pages/Updater.tsx | 5 +++-- src/renderer/util.ts | 13 +++++++------ 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/scripts/inject/injector.mts b/scripts/inject/injector.mts index 5913ad0b2..288365848 100644 --- a/scripts/inject/injector.mts +++ b/scripts/inject/injector.mts @@ -101,7 +101,7 @@ export const inject = async ( return false; } - const fileToCheck = join(dirname, "..", "..", prod ? "replugged.asar" : "dist-bundle/main.js"); + const fileToCheck = join(dirname, "..", "..", prod ? "replugged.asar" : "dist/main.js"); const fileToCheckExists = await stat(fileToCheck) .then(() => true) .catch(() => false); diff --git a/src/renderer/apis/settings.ts b/src/renderer/apis/settings.ts index 93708dd52..2cdfd9a51 100644 --- a/src/renderer/apis/settings.ts +++ b/src/renderer/apis/settings.ts @@ -57,8 +57,8 @@ export class SettingsManager, D extends ke ): K extends D ? NonNullable : F extends null | undefined - ? T[K] | undefined - : NonNullable | F { + ? T[K] | undefined + : NonNullable | F { if (typeof this.#settings === "undefined") { throw new Error(`Settings not loaded for namespace ${this.namespace}`); } diff --git a/src/renderer/coremods/commands/commands.ts b/src/renderer/coremods/commands/commands.ts index 7665c1f12..7377fe0f0 100644 --- a/src/renderer/coremods/commands/commands.ts +++ b/src/renderer/coremods/commands/commands.ts @@ -351,8 +351,8 @@ export function loadCommands(): void { listType === "enabled" ? enabledString : listType === "disabled" - ? disabledString - : `${enabledString}\n\n${disabledString}`; + ? disabledString + : `${enabledString}\n\n${disabledString}`; return { send, @@ -383,8 +383,8 @@ export function loadCommands(): void { listType === "enabled" ? enabledString : listType === "disabled" - ? disabledString - : `${enabledString}\n\n${disabledString}`; + ? disabledString + : `${enabledString}\n\n${disabledString}`; return { send, diff --git a/src/renderer/coremods/settings/pages/Updater.tsx b/src/renderer/coremods/settings/pages/Updater.tsx index 9473d39c7..f56d2b97d 100644 --- a/src/renderer/coremods/settings/pages/Updater.tsx +++ b/src/renderer/coremods/settings/pages/Updater.tsx @@ -22,8 +22,9 @@ const logger = Logger.coremod("Settings:Updater"); export const Updater = (): React.ReactElement => { const [checking, setChecking] = React.useState(false); - const [updatesAvailable, setUpdatesAvailable] = - React.useState>(getAvailableUpdates()); + const [updatesAvailable, setUpdatesAvailable] = React.useState< + Array + >(getAvailableUpdates()); const [updatePromises, setUpdatePromises] = React.useState>>({}); const [didInstallAll, setDidInstallAll] = React.useState(false); const [lastChecked, setLastChecked] = useSettingArray(updaterSettings, "lastChecked"); diff --git a/src/renderer/util.ts b/src/renderer/util.ts index b79a6ccc2..3ffed1f32 100644 --- a/src/renderer/util.ts +++ b/src/renderer/util.ts @@ -197,8 +197,8 @@ export function useSetting< value: K extends D ? NonNullable : F extends null | undefined - ? T[K] | undefined - : NonNullable | F; + ? T[K] | undefined + : NonNullable | F; onChange: (newValue: ValType) => void; } { const initial = settings.get(key, fallback); @@ -237,8 +237,8 @@ export function useSettingArray< K extends D ? NonNullable : F extends null | undefined - ? T[K] | undefined - : NonNullable | F, + ? T[K] | undefined + : NonNullable | F, (newValue: ValType) => void, ] { const { value, onChange } = useSetting(settings, key, fallback); @@ -256,8 +256,9 @@ type UnionToIntersection = (U extends never ? never : (k: U) => void) extends type ObjectType = Record; -type ExtractObjectType = - O extends Array ? UnionToIntersection : never; +type ExtractObjectType = O extends Array + ? UnionToIntersection + : never; export function virtualMerge(...objects: O): ExtractObjectType { const fallback = {}; From 890561f8cf48f65b4f1000c9b3c34e3ced200a0a Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Fri, 12 Jul 2024 01:51:44 +0530 Subject: [PATCH 13/17] prettier --- src/renderer/apis/settings.ts | 4 ++-- src/renderer/coremods/commands/commands.ts | 8 ++++---- src/renderer/coremods/settings/pages/Updater.tsx | 5 ++--- src/renderer/util.ts | 13 ++++++------- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/renderer/apis/settings.ts b/src/renderer/apis/settings.ts index 2cdfd9a51..93708dd52 100644 --- a/src/renderer/apis/settings.ts +++ b/src/renderer/apis/settings.ts @@ -57,8 +57,8 @@ export class SettingsManager, D extends ke ): K extends D ? NonNullable : F extends null | undefined - ? T[K] | undefined - : NonNullable | F { + ? T[K] | undefined + : NonNullable | F { if (typeof this.#settings === "undefined") { throw new Error(`Settings not loaded for namespace ${this.namespace}`); } diff --git a/src/renderer/coremods/commands/commands.ts b/src/renderer/coremods/commands/commands.ts index 7377fe0f0..7665c1f12 100644 --- a/src/renderer/coremods/commands/commands.ts +++ b/src/renderer/coremods/commands/commands.ts @@ -351,8 +351,8 @@ export function loadCommands(): void { listType === "enabled" ? enabledString : listType === "disabled" - ? disabledString - : `${enabledString}\n\n${disabledString}`; + ? disabledString + : `${enabledString}\n\n${disabledString}`; return { send, @@ -383,8 +383,8 @@ export function loadCommands(): void { listType === "enabled" ? enabledString : listType === "disabled" - ? disabledString - : `${enabledString}\n\n${disabledString}`; + ? disabledString + : `${enabledString}\n\n${disabledString}`; return { send, diff --git a/src/renderer/coremods/settings/pages/Updater.tsx b/src/renderer/coremods/settings/pages/Updater.tsx index f56d2b97d..9473d39c7 100644 --- a/src/renderer/coremods/settings/pages/Updater.tsx +++ b/src/renderer/coremods/settings/pages/Updater.tsx @@ -22,9 +22,8 @@ const logger = Logger.coremod("Settings:Updater"); export const Updater = (): React.ReactElement => { const [checking, setChecking] = React.useState(false); - const [updatesAvailable, setUpdatesAvailable] = React.useState< - Array - >(getAvailableUpdates()); + const [updatesAvailable, setUpdatesAvailable] = + React.useState>(getAvailableUpdates()); const [updatePromises, setUpdatePromises] = React.useState>>({}); const [didInstallAll, setDidInstallAll] = React.useState(false); const [lastChecked, setLastChecked] = useSettingArray(updaterSettings, "lastChecked"); diff --git a/src/renderer/util.ts b/src/renderer/util.ts index 3ffed1f32..b79a6ccc2 100644 --- a/src/renderer/util.ts +++ b/src/renderer/util.ts @@ -197,8 +197,8 @@ export function useSetting< value: K extends D ? NonNullable : F extends null | undefined - ? T[K] | undefined - : NonNullable | F; + ? T[K] | undefined + : NonNullable | F; onChange: (newValue: ValType) => void; } { const initial = settings.get(key, fallback); @@ -237,8 +237,8 @@ export function useSettingArray< K extends D ? NonNullable : F extends null | undefined - ? T[K] | undefined - : NonNullable | F, + ? T[K] | undefined + : NonNullable | F, (newValue: ValType) => void, ] { const { value, onChange } = useSetting(settings, key, fallback); @@ -256,9 +256,8 @@ type UnionToIntersection = (U extends never ? never : (k: U) => void) extends type ObjectType = Record; -type ExtractObjectType = O extends Array - ? UnionToIntersection - : never; +type ExtractObjectType = + O extends Array ? UnionToIntersection : never; export function virtualMerge(...objects: O): ExtractObjectType { const fallback = {}; From 63bc2306952c7da3c9f72040c9d82f0afc0c9857 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Fri, 12 Jul 2024 02:17:55 +0530 Subject: [PATCH 14/17] await --- scripts/inject/injector.mts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/inject/injector.mts b/scripts/inject/injector.mts index 288365848..716890dfb 100644 --- a/scripts/inject/injector.mts +++ b/scripts/inject/injector.mts @@ -226,7 +226,7 @@ export const smartInject = async ( result = cmd === "uninject" ? await uninject(platformModule, platform) - : inject(platformModule, platform, production); + : await inject(platformModule, platform, production); } else { const processName = PlatformNames[platform].replace(" ", ""); try { @@ -238,7 +238,7 @@ export const smartInject = async ( result = cmd === "uninject" ? await uninject(platformModule, platform) - : inject(platformModule, platform, production); + : await inject(platformModule, platform, production); if (((replug && cmd !== "uninject") || !replug) && processInfo) { const appDir = await platformModule.getAppDir(platform); switch (process.platform) { @@ -256,6 +256,7 @@ export const smartInject = async ( stdio: "ignore", }); break; + s; case "darwin": openProcess(`open -a ${PlatformNames[platform]}`); break; From ea52882e6d18c5f8d7c11f4706193762d96e0412 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Fri, 12 Jul 2024 02:19:47 +0530 Subject: [PATCH 15/17] FRCIK KEYBOARD SHORTCUT --- scripts/inject/injector.mts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/inject/injector.mts b/scripts/inject/injector.mts index 716890dfb..b98b77286 100644 --- a/scripts/inject/injector.mts +++ b/scripts/inject/injector.mts @@ -256,7 +256,6 @@ export const smartInject = async ( stdio: "ignore", }); break; - s; case "darwin": openProcess(`open -a ${PlatformNames[platform]}`); break; From f7f85aafef5676ffc05960846d9847bc19fb12a7 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Fri, 12 Jul 2024 02:36:12 +0530 Subject: [PATCH 16/17] right place --- scripts/inject/index.mts | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/scripts/inject/index.mts b/scripts/inject/index.mts index a5ae97a01..3e0eb3763 100644 --- a/scripts/inject/index.mts +++ b/scripts/inject/index.mts @@ -104,6 +104,14 @@ const run = async (cmd = ctx.getPositionalArg(2), replug = false): Promise `${AnsiEscapes.RED}An error occurred while trying to inject into Discord!${AnsiEscapes.RESET}`, ); console.error(e); + if ((e as { code: string }).code === "EBUSY") { + console.log( + `\nYou now have to completely close the Discord client, from the system tray or through the task manager.\n +To unplug from a different platform, use the following syntax: ${AnsiEscapes.BOLD}${ + AnsiEscapes.GREEN + }${getCommand({ action: "unplug", prod })}${AnsiEscapes.RESET}`, + ); + } process.exit(exitCode); } if (result) { @@ -114,12 +122,6 @@ const run = async (cmd = ctx.getPositionalArg(2), replug = false): Promise } :D${AnsiEscapes.RESET}`, "\n", ); - console.log( - `You now have to completely close the Discord client, from the system tray or through the task manager.\n -To plug into a different platform, use the following syntax: ${AnsiEscapes.BOLD}${ - AnsiEscapes.GREEN - }${getCommand({ action: replug ? "replug" : "plug", prod })}${AnsiEscapes.RESET}`, - ); } else { process.exit(exitCode); } @@ -131,6 +133,14 @@ To plug into a different platform, use the following syntax: ${AnsiEscapes.BOLD} `${AnsiEscapes.RED}An error occurred while trying to uninject from Discord!${AnsiEscapes.RESET}`, ); console.error(e); + if ((e as { code: string }).code === "EBUSY") { + console.log( + `\nYou now have to completely close the Discord client, from the system tray or through the task manager.\n +To unplug from a different platform, use the following syntax: ${AnsiEscapes.BOLD}${ + AnsiEscapes.GREEN + }${getCommand({ action: "unplug", prod })}${AnsiEscapes.RESET}`, + ); + } process.exit(exitCode); } if (result) { @@ -142,12 +152,6 @@ To plug into a different platform, use the following syntax: ${AnsiEscapes.BOLD} `${AnsiEscapes.BOLD}${AnsiEscapes.GREEN}Replugged has been successfully unplugged${AnsiEscapes.RESET}`, "\n", ); - console.log( - `You now have to completely close the Discord client, from the system tray or through the task manager.\n -To unplug from a different platform, use the following syntax: ${AnsiEscapes.BOLD}${ - AnsiEscapes.GREEN - }${getCommand({ action: "unplug", prod })}${AnsiEscapes.RESET}`, - ); } } } else if (cmd === "reinject") { From 4da86b01790cdcfdd55ef2c95390e3a5b65c1a21 Mon Sep 17 00:00:00 2001 From: Federico <38290480+FedeIlLeone@users.noreply.github.com> Date: Fri, 12 Jul 2024 12:21:12 +0200 Subject: [PATCH 17/17] fix: find parent pid on windows and kill all processes on linux --- cspell.json | 1 - scripts/inject/index.mts | 44 +++++++++++++----------- scripts/inject/injector.mts | 67 ++++++++++++++++++++++--------------- scripts/inject/types.mts | 2 +- scripts/inject/util.mts | 52 ++++++++++++++++------------ 5 files changed, 95 insertions(+), 71 deletions(-) diff --git a/cspell.json b/cspell.json index 689430bec..ec580088b 100644 --- a/cspell.json +++ b/cspell.json @@ -30,7 +30,6 @@ "gifv", "gpgsign", "HighlightJS", - "IMAGENAME", "installdir", "Jsonifiable", "konami", diff --git a/scripts/inject/index.mts b/scripts/inject/index.mts index 3e0eb3763..af99fcffb 100644 --- a/scripts/inject/index.mts +++ b/scripts/inject/index.mts @@ -3,15 +3,15 @@ import "./checks/elevate.mjs"; import "./checks/env.mjs"; import { join } from "path"; -import { AnsiEscapes, getCommand } from "./util.mjs"; import { smartInject } from "./injector.mjs"; +import { AnsiEscapes, getCommand } from "./util.mjs"; +import { createContext, getPositionalArg } from "@marshift/argus"; +import { existsSync } from "fs"; import * as darwin from "./platforms/darwin.mjs"; import * as linux from "./platforms/linux.mjs"; import * as win32 from "./platforms/win32.mjs"; -import { DiscordPlatform } from "./types.mjs"; -import { existsSync } from "fs"; -import { createContext, getPositionalArg } from "@marshift/argus"; +import type { DiscordPlatform } from "./types.mjs"; const platformModules = { darwin, @@ -104,14 +104,6 @@ const run = async (cmd = ctx.getPositionalArg(2), replug = false): Promise `${AnsiEscapes.RED}An error occurred while trying to inject into Discord!${AnsiEscapes.RESET}`, ); console.error(e); - if ((e as { code: string }).code === "EBUSY") { - console.log( - `\nYou now have to completely close the Discord client, from the system tray or through the task manager.\n -To unplug from a different platform, use the following syntax: ${AnsiEscapes.BOLD}${ - AnsiEscapes.GREEN - }${getCommand({ action: "unplug", prod })}${AnsiEscapes.RESET}`, - ); - } process.exit(exitCode); } if (result) { @@ -122,6 +114,16 @@ To unplug from a different platform, use the following syntax: ${AnsiEscapes.BOL } :D${AnsiEscapes.RESET}`, "\n", ); + console.log( + `${ + noRelaunch + ? `You now have to completely close the Discord client, from the system tray or through the task manager.\n` + : "Your Discord client has been restarted automatically.\n" + } +To plug into a different platform, use the following syntax: ${AnsiEscapes.BOLD}${ + AnsiEscapes.GREEN + }${getCommand({ action: replug ? "replug" : "plug", prod })}${AnsiEscapes.RESET}`, + ); } else { process.exit(exitCode); } @@ -133,14 +135,6 @@ To unplug from a different platform, use the following syntax: ${AnsiEscapes.BOL `${AnsiEscapes.RED}An error occurred while trying to uninject from Discord!${AnsiEscapes.RESET}`, ); console.error(e); - if ((e as { code: string }).code === "EBUSY") { - console.log( - `\nYou now have to completely close the Discord client, from the system tray or through the task manager.\n -To unplug from a different platform, use the following syntax: ${AnsiEscapes.BOLD}${ - AnsiEscapes.GREEN - }${getCommand({ action: "unplug", prod })}${AnsiEscapes.RESET}`, - ); - } process.exit(exitCode); } if (result) { @@ -152,6 +146,16 @@ To unplug from a different platform, use the following syntax: ${AnsiEscapes.BOL `${AnsiEscapes.BOLD}${AnsiEscapes.GREEN}Replugged has been successfully unplugged${AnsiEscapes.RESET}`, "\n", ); + console.log( + `${ + noRelaunch + ? `You now have to completely close the Discord client, from the system tray or through the task manager.\n` + : "Your Discord client has been restarted automatically.\n" + } +To unplug from a different platform, use the following syntax: ${AnsiEscapes.BOLD}${ + AnsiEscapes.GREEN + }${getCommand({ action: "unplug", prod })}${AnsiEscapes.RESET}`, + ); } } } else if (cmd === "reinject") { diff --git a/scripts/inject/injector.mts b/scripts/inject/injector.mts index b98b77286..1403788d5 100644 --- a/scripts/inject/injector.mts +++ b/scripts/inject/injector.mts @@ -1,23 +1,23 @@ +import { execSync } from "child_process"; +import { existsSync } from "fs"; import { chown, copyFile, mkdir, rename, rm, stat, writeFile } from "fs/promises"; import path, { join, sep } from "path"; import { fileURLToPath } from "url"; +import { CONFIG_PATH } from "../../src/util.mjs"; +import { entryPoint as argEntryPoint, exitCode } from "./index.mjs"; +import type { DiscordPlatform, PlatformModule, ProcessInfo } from "./types.mjs"; import { AnsiEscapes, - GetUserData, PlatformNames, getCommand, getProcessInfoByName, + getUserData, killProcessByPID, openProcess, } from "./util.mjs"; -import { entryPoint as argEntryPoint, exitCode } from "./index.mjs"; -import { execSync } from "child_process"; -import { DiscordPlatform, PlatformModule, ProcessInfo } from "./types.mjs"; -import { CONFIG_PATH } from "../../src/util.mjs"; -import { existsSync } from "fs"; const dirname = path.dirname(fileURLToPath(import.meta.url)); -let processInfo: ProcessInfo; +let processInfo: ProcessInfo | ProcessInfo[] | null; export const isDiscordInstalled = async (appDir: string, silent?: boolean): Promise => { try { @@ -201,15 +201,23 @@ export const uninject = async ( return false; } - await rm(appDir, { recursive: true, force: true }); - await rename(join(appDir, "..", "app.orig.asar"), appDir); - // For discord_arch_electron - if (existsSync(join(appDir, "..", "app.orig.asar.unpacked"))) { - await rename( - join(appDir, "..", "app.orig.asar.unpacked"), - join(appDir, "..", "app.asar.unpacked"), + try { + await rm(appDir, { recursive: true, force: true }); + await rename(join(appDir, "..", "app.orig.asar"), appDir); + // For discord_arch_electron + if (existsSync(join(appDir, "..", "app.orig.asar.unpacked"))) { + await rename( + join(appDir, "..", "app.orig.asar.unpacked"), + join(appDir, "..", "app.asar.unpacked"), + ); + } + } catch { + console.error( + `${AnsiEscapes.RED}Failed to rename app.asar while unplugging. If Discord is open, make sure it is closed.${AnsiEscapes.RESET}`, ); + process.exit(exitCode); } + return true; }; @@ -222,23 +230,27 @@ export const smartInject = async ( noRelaunch: boolean, ): Promise => { let result; - if (noRelaunch) { - result = - cmd === "uninject" - ? await uninject(platformModule, platform) - : await inject(platformModule, platform, production); - } else { - const processName = PlatformNames[platform].replace(" ", ""); + + const processName = PlatformNames[platform].replace(" ", ""); + if (!noRelaunch) { try { if ((replug && cmd === "uninject") || !replug) { processInfo = getProcessInfoByName(processName)!; - await killProcessByPID(processInfo?.pid); + if (Array.isArray(processInfo)) { + await Promise.all(processInfo.map((info) => killProcessByPID(info.pid))); + } else { + await killProcessByPID(processInfo?.pid); + } } } catch {} - result = - cmd === "uninject" - ? await uninject(platformModule, platform) - : await inject(platformModule, platform, production); + } + + result = + cmd === "uninject" + ? await uninject(platformModule, platform) + : await inject(platformModule, platform, production); + + if (!noRelaunch) { if (((replug && cmd !== "uninject") || !replug) && processInfo) { const appDir = await platformModule.getAppDir(platform); switch (process.platform) { @@ -251,7 +263,7 @@ export const smartInject = async ( break; case "linux": openProcess(join(appDir, "..", "..", processName), [], { - ...GetUserData(), + ...getUserData(), detached: true, stdio: "ignore", }); @@ -262,5 +274,6 @@ export const smartInject = async ( } } } + return result; }; diff --git a/scripts/inject/types.mts b/scripts/inject/types.mts index e878668b1..ad7671975 100644 --- a/scripts/inject/types.mts +++ b/scripts/inject/types.mts @@ -8,7 +8,7 @@ export interface PlatformModule { export interface ProcessInfo { pid: number; - cmd: string[]; + ppid: number; } export interface UserData { diff --git a/scripts/inject/util.mts b/scripts/inject/util.mts index 2856e975d..0e97ff20a 100644 --- a/scripts/inject/util.mts +++ b/scripts/inject/util.mts @@ -1,5 +1,5 @@ -import { SpawnOptions, execSync, spawn } from "child_process"; -import { DiscordPlatform, ProcessInfo, UserData } from "./types.mjs"; +import { type SpawnOptions, execSync, spawn } from "child_process"; +import type { DiscordPlatform, ProcessInfo, UserData } from "./types.mjs"; export const AnsiEscapes = { RESET: "\x1b[0m", @@ -31,32 +31,40 @@ export const getCommand = ({ return cmd; }; -export const getProcessInfoByName = (processName: string): ProcessInfo | null => { - if (process.platform === "win32") { - const command = `tasklist /FI "IMAGENAME eq ${processName}.exe" /FO CSV`; - const output = execSync(command).toString().trim(); +export const getProcessInfoByName = (processName: string): ProcessInfo | ProcessInfo[] | null => { + try { + const isWindows = process.platform === "win32"; + const command = isWindows + ? `wmic process where (Name="${processName}.exe") get ProcessId,ParentProcessId /FORMAT:CSV` + : `ps -eo cmd,ppid,pid | grep -E "(^|/)${processName}(\\s|$)" | grep -v grep`; + const output = execSync(command).toString(); - const lines = output.split("\r\n"); - if (lines.length <= 2) { + if (!output.trim()) { return null; } - const [_header, data] = lines.slice(0, 2); - const [name, pid] = data.split('","'); - - return { pid: Number(pid), cmd: name.substring(1).split(/\s+/) }; - } - const command = `ps -eo pid,command | grep -E "(^|/)${processName}(\\s|$)" | grep -v grep`; - - const output = execSync(command).toString().trim(); + const lines = output + .trim() + .split(isWindows ? "\r\r\n" : "\n") + .slice(1); + const processInfo = lines.map((line) => { + const parts = isWindows ? line.split(",") : line.trim().split(/\s+/); + return { + ppid: parseInt(parts[1], 10), + pid: parseInt(parts[2], 10), + }; + }); - if (output.length === 0) { + if (isWindows) { + const parentPIDs = processInfo.map((process) => process.ppid); + const mainProcess = processInfo.find((process) => parentPIDs.includes(process.pid)); + return mainProcess || null; + } else { + return processInfo || null; + } + } catch { return null; } - - const [pid, ...cmd] = output.split(/\s+/); - - return { pid: Number(pid), cmd }; }; export const killCheckProcessExists = (pid: number): boolean => { @@ -91,7 +99,7 @@ export const openProcess = (command: string, args?: string[], options?: SpawnOpt : spawn(command, args ?? [], options ?? {}).unref()); }; -export const GetUserData = (): UserData => { +export const getUserData = (): UserData => { const name = execSync("logname", { encoding: "utf8" }).toString().trim().replace(/\n$/, ""); const env = Object.assign({}, process.env, { HOME: `/home/${name}` }); const uid = execSync(`id -u ${name}`, { encoding: "utf8" }).toString().trim().replace(/\n$/, "");