aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md17
-rw-r--r--install.ps190
-rwxr-xr-xinstall.sh75
-rw-r--r--package.json5
-rw-r--r--scripts/patcher/common.js297
-rw-r--r--scripts/patcher/install.js111
-rw-r--r--scripts/patcher/uninstall.js59
-rw-r--r--uninstall.ps173
-rwxr-xr-xuninstall.sh9
9 files changed, 481 insertions, 255 deletions
diff --git a/README.md b/README.md
index ef92682..10db1e6 100644
--- a/README.md
+++ b/README.md
@@ -15,11 +15,13 @@ A Discord client mod that does things differently
## Installing
-If you can't follow the following instructions, please just use BetterDiscord. This was never meant to be a noob friendly mod.
+If you can't follow the following instructions, please just use BetterDiscord.
+This was never meant to be a noob friendly mod.
Install [Node.js](https://nodejs.org/en/download/) and [git](https://git-scm.com/downloads)
-Open a Terminal and run the following commands. If any of them failed, you didn't properly install Node.js and git (see above).
+Open a Terminal and run the following commands.
+If any of them failed, you didn't properly install Node.js and git (see above).
> :warning: On Windows, DO NOT run the terminal as Administrator. If you open it and the path says system32, you opened it as Administrator.
```sh
@@ -31,12 +33,11 @@ pnpm build
```
Don't close your terminal just yet!
-The builds are now in the dist/ folder (Vencord/dist). Most importantly, you will need `dist/patcher.js`
+Now to patch vencord into your Discord client, run the following command and follow the interactive prompt.
-Now download [X1nto's installer](https://github.com/X1nto/VencordInstaller/releases/latest) for your platform. Download it to the Vencord folder.
-Run it via terminal: `VencordInstaller.exe` on Windows or `chmod +x vencord_installer && ./vencord_installer` on Mac.
-
-Follow along with the prompts. Once you are prompted for the patcher, enter `dist/patcher.js`.
+```sh
+pnpm patch
+```
Now fully close Discord. Start and confirm Vencord successfully installed by checking if you have a new Vencord section in Settings.
@@ -44,6 +45,8 @@ If you ever need to get back to the Vencord folder, just open a new terminal and
All plugins are disabled by default, so your first step should be opening Settings and enabling the plugins you want.
+You can unpatch Vencord using `pnpm unpatch`
+
## Installing on Browser
diff --git a/install.ps1 b/install.ps1
deleted file mode 100644
index 88a8850..0000000
--- a/install.ps1
+++ /dev/null
@@ -1,90 +0,0 @@
-# Vencord Windows Installer
-
-$patcher = "$PWD\dist\patcher.js"
-$patcher_safe = $patcher -replace '\\', '\\'
-
-$APP_PATCH = @"
-require("$patcher_safe");
-require("../app.asar");
-"@
-
-$PACKAGE_JSON = @"
-{
- "main": "index.js",
- "name": "discord"
-}
-"@
-
-$branch_paths = Get-ChildItem -Directory -Path $env:LOCALAPPDATA |
- Select-String -Pattern "Discord\w*" -AllMatches |
- Select-String -Pattern "DiscordGames" -NotMatch # Ignore DiscordGames folder
-
-$branches = @()
-
-foreach ($branch in $branch_paths) {
- $branch = $branch.Line.Split("\")[-1]
-
- if ($branch -eq "Discord") {
- $branch = "Discord Stable"
- } else {
- $branch = $branch.Replace("Discord", "Discord ")
- }
-
- $branches = $branches + $branch
-}
-
-$branch_count = $branches.Count
-
-Write-Output "Found $branch_count Branches"
-Write-Output "====================================="
-Write-Output "===== Select a Branch to patch ======"
-
-$i = 0
-foreach ($branch in $branches) {
- Write-Output "=== $i. $branch"
- $i++
-}
-
-Write-Output "====================================="
-$pos = Read-Host "Enter a number"
-
-if ($null -eq $branches[$pos]) {
- Write-Output "Invalid branch selection"
- exit
-}
-
-$branch = $branches.Get($pos)
-$discord_root = $branch_paths.Get($pos)
-
-Write-Output "`nPatching $branch"
-
-$app_folders = Get-ChildItem -Directory -Path $discord_root |
- Select-String -Pattern "app-"
-
-foreach ($folder in $app_folders)
-{
- $version = [regex]::match($folder, 'app-([\d\.]+)').Groups[1].Value
- Write-Output "Patching Version $version"
-
- $resources = "$folder\resources"
- if (-not(Test-Path -Path "$resources")) {
- Write-Error "Resources folder does not exist. Outdated version?`n"
- continue
- }
- if (-not(Test-Path -Path "$resources\app.asar")) {
- Write-Error "Failed to find app.asar in $folder`n"
- continue
- }
-
- $app = "$resources\app"
- if (Test-Path -Path $app) {
- Write-Error "Are you already patched? App folder already exists at $resources`n"
- continue
- }
-
- $null = New-Item -Path $app -ItemType Directory
- $null = Tee-Object -InputObject $APP_PATCH -FilePath "$app\index.js"
- $null = Tee-Object -InputObject $PACKAGE_JSON -FilePath "$app\package.json"
-
- Write-Output "Patched $branch (version $version) successfully"
-}
diff --git a/install.sh b/install.sh
deleted file mode 100755
index 945d2de..0000000
--- a/install.sh
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/bin/sh
-#
-# Super simple installer. You should probably run this as root.
-# If you are getting permission issues, this is probably why.
-#
-# If this doesn't work for you, or you're not on Linux, just
-# - locate your Discord folder
-# - inside the resources folder, create a new folder "app"
-# - inside app create the files index.js and package.json.
-# See the two tee commands at the end of the file for their contents
-
-patcher="$PWD/dist/patcher.js"
-
-discord_bin="$(which discord)"
-discord_actual="$(readlink "$discord_bin")"
-
-if [ -z "$discord_actual" ]; then
- case "$(head -n1 "$discord_bin")" in
- # has shebang?
- \#!/*)
- # Wrapper script, assume 2nd line has exec electron call and try to match asar path
- path="$(tail -1 "$discord_bin" | grep -Eo "\S+/app.asar" | sed 's/${name}/discord/')"
- if [ -z "$path" ]; then
- echo "Unsupported Install. $discord_bin is wrapper script but last line isn't exec call?"
- exit
- elif [ -e "$path" ]; then
- discord="$(dirname "$path")"
- else
- echo "Unsupported Install. $path not found"
- exit 1
- fi
- ;;
- *)
- echo "Unsupported Install. $discord_bin is neither symlink nor a wrapper script.";
- exit 1
- ;;
- esac
-else
- discord="$(dirname "$discord_actual")"
-fi
-
-resources="$discord/resources"
-app="$resources/app"
-app_asar="app.asar"
-
-if [ ! -e "$resources" ]; then
- if [ -e "$discord/app.asar.unpacked" ]; then
- # System Electron Install
- mv "$discord/app.asar" "$discord/_app.asar"
- mv "$discord/app.asar.unpacked" "$discord/_app.asar.unpacked"
- app="$discord/app.asar"
- app_asar="_app.asar"
- else
- echo "Unsupported Install. $discord has no resources folder but also isn't system electron install"
- exit
- fi
-fi
-
-if [ -e "$app" ]; then
- echo "app folder exists. Looks like your Discord is already modified."
- exit
-fi
-
-mkdir "$app"
-tee > "$app/index.js" << EOF
-require("$patcher");
-require("../$app_asar");
-EOF
-
-tee > "$app/package.json" << EOF
-{
- "main": "index.js",
- "name": "discord"
-}
-EOF
diff --git a/package.json b/package.json
index 39ac699..515abae 100644
--- a/package.json
+++ b/package.json
@@ -8,12 +8,15 @@
"yazl": "^2.5.1"
},
"dependencies": {
+ "console-menu": "^0.1.0",
"discord-types": "^1.3.26",
"electron-devtools-installer": "^3.2.0"
},
"scripts": {
"buildWeb": "node buildWeb.mjs",
"build": "node build.mjs",
- "watch": "node build.mjs --watch"
+ "watch": "node build.mjs --watch",
+ "patch": "node scripts/patcher/install.js",
+ "unpatch": "node scripts/patcher/uninstall.js"
}
}
diff --git a/scripts/patcher/common.js b/scripts/patcher/common.js
new file mode 100644
index 0000000..94cb383
--- /dev/null
+++ b/scripts/patcher/common.js
@@ -0,0 +1,297 @@
+const path = require("path");
+const readline = require("readline");
+const fs = require("fs");
+const menu = require("console-menu");
+
+const BRANCH_NAMES = [
+ "Discord",
+ "DiscordPTB",
+ "DiscordCanary",
+ "DiscordDevelopment",
+ "discord",
+ "discordptb",
+ "discordcanary",
+ "discorddevelopment",
+ "discord-ptb",
+ "discord-canary",
+ "discord-development",
+ // Flatpak
+ "com.discordapp.Discord",
+ "com.discordapp.DiscordPTB",
+ "com.discordapp.DiscordCanary",
+ "com.discordapp.DiscordDevelopment",
+];
+
+const MACOS_DISCORD_DIRS = [
+ "Discord.app",
+ "Discord PTB.app",
+ "Discord Canary.app",
+ "Discord Development.app",
+];
+
+if (process.platform === "linux" && process.env.SUDO_USER) {
+ process.env.HOME = fs
+ .readFileSync("/etc/passwd", "utf-8")
+ .match(new RegExp(`^${process.env.SUDO_USER}.+$`, "m"))[0]
+ .split(":")[5];
+}
+
+const LINUX_DISCORD_DIRS = [
+ "/usr/share",
+ "/usr/lib64",
+ "/opt",
+ `${process.env.HOME}/.local/share`,
+ "/var/lib/flatpak/app",
+ `${process.env.HOME}/.local/share/flatpak/app`,
+];
+
+const FLATPAK_NAME_MAPPING = {
+ DiscordCanary: "discord-canary",
+ DiscordPTB: "discord-ptb",
+ DiscordDevelopment: "discord-development",
+ Discord: "discord",
+};
+
+const ENTRYPOINT = path
+ .join(process.cwd(), "dist", "patcher.js")
+ .replace(/\\/g, "/");
+
+function question(question) {
+ const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ terminal: false,
+ });
+
+ return new Promise((resolve) => {
+ rl.question(question, (answer) => {
+ rl.close();
+ resolve(answer);
+ });
+ });
+}
+
+async function getMenuItem(installations) {
+ let menuItems = installations.map((info) => ({
+ title: info.patched ? "[MODIFIED] " + info.location : info.location,
+ info,
+ }));
+
+ if (menuItems.length === 0) {
+ console.log("No Discord installations found.");
+ process.exit(1);
+ }
+
+ const result = await menu(
+ [...menuItems, { title: "Exit without patching", exit: true }],
+ {
+ header: "Select a Discord installation to patch:",
+ border: true,
+ helpMessage:
+ "Use the up/down arrow keys to select an option. " +
+ "Press ENTER to confirm.",
+ }
+ );
+
+ if (!result || !result.info || result.exit) {
+ console.log("No installation selected.");
+ process.exit(0);
+ }
+
+ if (result.info.patched) {
+ const answer = await question(
+ "This installation has already been modified. Overwrite? [Y/n]: "
+ );
+
+ if (!["y", "yes", "yeah", ""].includes(answer.toLowerCase())) {
+ console.log("Not patching.");
+ process.exit(0);
+ }
+ }
+
+ return result.info;
+}
+
+function getWindowsDirs() {
+ const dirs = [];
+ for (const dir of fs.readdirSync(process.env.LOCALAPPDATA)) {
+ if (!BRANCH_NAMES.includes(dir)) continue;
+
+ const location = path.join(process.env.LOCALAPPDATA, dir);
+ if (!fs.statSync(location).isDirectory()) continue;
+
+ const appDirs = fs
+ .readdirSync(location, { withFileTypes: true })
+ .filter((file) => file.isDirectory())
+ .filter((file) => file.name.startsWith("app-"))
+ .map((file) => path.join(location, file.name));
+
+ let versions = [];
+ let patched = false;
+
+ for (const fqAppDir of appDirs) {
+ const resourceDir = path.join(fqAppDir, "resources");
+ if (!fs.existsSync(path.join(resourceDir, "app.asar"))) {
+ continue;
+ }
+ const appDir = path.join(resourceDir, "app");
+ if (fs.existsSync(appDir)) {
+ patched = true;
+ }
+ versions.push({
+ path: appDir,
+ name: /app-([0-9\.]+)/.exec(fqAppDir)[1],
+ });
+ }
+
+ if (appDirs.length) {
+ dirs.push({
+ branch: dir,
+ patched,
+ location,
+ versions,
+ arch: "win32",
+ flatpak: false,
+ });
+ }
+ }
+ return dirs;
+}
+
+function getDarwinDirs() {
+ const dirs = [];
+ for (const dir of fs.readdirSync("/Applications")) {
+ if (!MACOS_DISCORD_DIRS.includes(dir)) continue;
+
+ const location = path.join("/Applications", dir, "Contents");
+ if (!fs.existsSync(location)) continue;
+ if (!fs.statSync(location).isDirectory()) continue;
+
+ const appDirs = fs
+ .readdirSync(location, { withFileTypes: true })
+ .filter((file) => file.isDirectory())
+ .filter((file) => file.name.startsWith("Resources"))
+ .map((file) => path.join(location, file.name));
+
+ let versions = [];
+ let patched = false;
+
+ for (const resourceDir of appDirs) {
+ if (!fs.existsSync(path.join(resourceDir, "app.asar"))) {
+ continue;
+ }
+ const appDir = path.join(resourceDir, "app");
+ if (fs.existsSync(appDir)) {
+ patched = true;
+ }
+
+ versions.push({
+ path: appDir,
+ name: null, // MacOS installs have no version number
+ });
+ }
+
+ if (appDirs.length) {
+ dirs.push({
+ branch: dir,
+ patched,
+ location,
+ versions,
+ arch: "win32",
+ });
+ }
+ }
+ return dirs;
+}
+
+function getLinuxDirs() {
+ const dirs = [];
+ for (const dir of LINUX_DISCORD_DIRS) {
+ if (!fs.existsSync(dir)) continue;
+ for (const branch of fs.readdirSync(dir)) {
+ if (!BRANCH_NAMES.includes(branch)) continue;
+
+ const location = path.join(dir, branch);
+ if (!fs.statSync(location).isDirectory()) continue;
+
+ const isFlatpak = location.includes("/flatpak/");
+
+ let appDirs = [];
+
+ if (isFlatpak) {
+ const fqDir = path.join(location, "current", "active", "files");
+ if (!/com\.discordapp\.(\w+)\//.test(fqDir)) continue;
+ const branchName = /com\.discordapp\.(\w+)\//.exec(fqDir)[1];
+ if (!Object.keys(FLATPAK_NAME_MAPPING).includes(branchName)) {
+ continue;
+ }
+ const appDir = path.join(
+ fqDir,
+ FLATPAK_NAME_MAPPING[branchName]
+ );
+
+ if (!fs.existsSync(appDir)) continue;
+ if (!fs.statSync(appDir).isDirectory()) continue;
+
+ const resourceDir = path.join(appDir, "resources");
+
+ appDirs.push(resourceDir);
+ } else {
+ appDirs = fs
+ .readdirSync(location, { withFileTypes: true })
+ .filter((file) => file.isDirectory())
+ .filter(
+ (file) =>
+ file.name.startsWith("app-") ||
+ file.name === "resources"
+ )
+ .map((file) => path.join(location, file.name));
+ }
+
+ let versions = [];
+ let patched = false;
+
+ for (const resourceDir of appDirs) {
+ if (!fs.existsSync(path.join(resourceDir, "app.asar"))) {
+ continue;
+ }
+ const appDir = path.join(resourceDir, "app");
+ if (fs.existsSync(appDir)) {
+ patched = true;
+ }
+
+ const version = /app-([0-9\.]+)/.exec(resourceDir);
+
+ versions.push({
+ path: appDir,
+ name: version && version.length > 1 ? version[1] : null,
+ });
+ }
+
+ if (appDirs.length) {
+ dirs.push({
+ branch,
+ patched,
+ location,
+ versions,
+ arch: "linux",
+ isFlatpak,
+ });
+ }
+ }
+ }
+ return dirs;
+}
+
+module.exports = {
+ BRANCH_NAMES,
+ MACOS_DISCORD_DIRS,
+ LINUX_DISCORD_DIRS,
+ FLATPAK_NAME_MAPPING,
+ ENTRYPOINT,
+ question,
+ getMenuItem,
+ getWindowsDirs,
+ getDarwinDirs,
+ getLinuxDirs,
+};
diff --git a/scripts/patcher/install.js b/scripts/patcher/install.js
new file mode 100644
index 0000000..42ba47f
--- /dev/null
+++ b/scripts/patcher/install.js
@@ -0,0 +1,111 @@
+const path = require("path");
+const fs = require("fs");
+const { execSync } = require("child_process");
+
+console.log("\nVencord Installer\n");
+
+if (!fs.existsSync(path.join(process.cwd(), "node_modules"))) {
+ console.log("You need to install dependencies first. Run:", "pnpm install");
+ process.exit(1);
+}
+
+if (!fs.existsSync(path.join(process.cwd(), "dist", "patcher.js"))) {
+ console.log("You need to build the project first. Run:", "pnpm build");
+ process.exit(1);
+}
+
+const {
+ getMenuItem,
+ getWindowsDirs,
+ getDarwinDirs,
+ getLinuxDirs,
+ ENTRYPOINT,
+} = require("./common");
+
+switch (process.platform) {
+ case "win32":
+ install(getWindowsDirs());
+ break;
+ case "darwin":
+ install(getDarwinDirs());
+ break;
+ case "linux":
+ install(getLinuxDirs());
+ break;
+ default:
+ console.log("Unknown OS");
+ break;
+}
+
+async function install(installations) {
+ const selected = await getMenuItem(installations);
+
+ // Attempt to give flatpak perms
+ if (selected.isFlatpak) {
+ try {
+ const branch = selected.branch;
+ const cwd = process.cwd();
+ const globalCmd = `flatpak override ${branch} --filesystem=${cwd}`;
+ const userCmd = `flatpak override --user ${branch} --filesystem=${cwd}`;
+ const cmd = selected.location.startsWith("/home")
+ ? userCmd
+ : globalCmd;
+ execSync(cmd);
+ console.log("Successfully gave write perms to Discord Flatpak.");
+ } catch (e) {
+ console.log("Failed to give write perms to Discord Flatpak.");
+ console.log(
+ "Try running this script as an administrator:",
+ "sudo pnpm patch"
+ );
+ process.exit(1);
+ }
+ }
+
+ for (const version of selected.versions) {
+ const dir = version.path;
+ // Check if we have write perms to the install directory...
+ try {
+ fs.accessSync(selected.location, fs.constants.W_OK);
+ } catch (e) {
+ console.error("No write access to", selected.location);
+ console.error(
+ "Try running this script as an administrator:",
+ "sudo pnpm patch"
+ );
+ process.exit(1);
+ }
+ if (fs.existsSync(dir) && fs.lstatSync(dir).isDirectory()) {
+ fs.rmSync(dir, { recursive: true });
+ }
+ if (!fs.existsSync(dir)) {
+ fs.mkdirSync(dir, { recursive: true });
+ }
+
+ fs.writeFileSync(
+ path.join(dir, "index.js"),
+ `require("${ENTRYPOINT}"); require("../app.asar");`
+ );
+ fs.writeFileSync(
+ path.join(dir, "package.json"),
+ JSON.stringify({
+ name: "discord",
+ main: "index.js",
+ })
+ );
+
+ const requiredFiles = ["index.js", "package.json"];
+
+ if (requiredFiles.every((f) => fs.existsSync(path.join(dir, f)))) {
+ console.log(
+ "Successfully patched",
+ version.name
+ ? `${selected.branch} ${version.name}`
+ : selected.branch
+ );
+ } else {
+ console.log("Failed to patch", dir);
+ console.log("Files in directory:", fs.readdirSync(dir));
+ }
+ }
+}
diff --git a/scripts/patcher/uninstall.js b/scripts/patcher/uninstall.js
new file mode 100644
index 0000000..c470c64
--- /dev/null
+++ b/scripts/patcher/uninstall.js
@@ -0,0 +1,59 @@
+const path = require("path");
+const fs = require("fs");
+
+console.log("\nVencord Uninstaller\n");
+
+if (!fs.existsSync(path.join(process.cwd(), "node_modules"))) {
+ console.log("You need to install dependencies first. Run:", "pnpm install");
+ process.exit(1);
+}
+
+const {
+ getMenuItem,
+ getWindowsDirs,
+ getDarwinDirs,
+ getLinuxDirs,
+} = require("./common");
+
+switch (process.platform) {
+ case "win32":
+ uninstall(getWindowsDirs());
+ break;
+ case "darwin":
+ uninstall(getDarwinDirs());
+ break;
+ case "linux":
+ uninstall(getLinuxDirs());
+ break;
+ default:
+ console.log("Unknown OS");
+ break;
+}
+
+async function uninstall(installations) {
+ const selected = await getMenuItem(installations);
+
+ for (const version of selected.versions) {
+ const dir = version.path;
+ // Check if we have write perms to the install directory...
+ try {
+ fs.accessSync(selected.location, fs.constants.W_OK);
+ } catch (e) {
+ console.error("No write access to", selected.location);
+ console.error(
+ "Try running this script as an administrator:",
+ "sudo pnpm unpatch"
+ );
+ process.exit(1);
+ }
+ if (fs.existsSync(dir)) {
+ fs.rmSync(dir, { recursive: true });
+ }
+ console.log(
+ "Successfully unpatched",
+ version.name
+ ? `${selected.branch} ${version.name}`
+ : selected.branch
+ );
+ }
+}
diff --git a/uninstall.ps1 b/uninstall.ps1
deleted file mode 100644
index 409b20f..0000000
--- a/uninstall.ps1
+++ /dev/null
@@ -1,73 +0,0 @@
-# Vencord Uninstaller
-
-$branch_paths = Get-ChildItem -Directory -Path $env:LOCALAPPDATA |
- Select-String -Pattern "Discord\w*" -AllMatches |
- Select-String -Pattern "DiscordGames" -NotMatch # Ignore DiscordGames folder
-
-$branches = @()
-
-foreach ($branch in $branch_paths) {
- $branch = $branch.Line.Split("\")[-1]
-
- if ($branch -eq "Discord") {
- $branch = "Discord Stable"
- } else {
- $branch = $branch.Replace("Discord", "Discord ")
- }
-
- $branches = $branches + $branch
-}
-
-$branch_count = $branches.Count
-
-Write-Output "Found $branch_count Branches"
-Write-Output "====================================="
-Write-Output "===== Select a Branch to unpatch ======"
-
-$i = 0
-foreach ($branch in $branches) {
- Write-Output "=== $i. $branch"
- $i++
-}
-
-Write-Output "====================================="
-$pos = Read-Host "Enter a number"
-
-if ($null -eq $branches[$pos]) {
- Write-Output "Invalid branch selection"
- exit
-}
-
-$branch = $branches.Get($pos)
-$discord_root = $branch_paths.Get($pos)
-
-Write-Output "`nUnpatch $branch"
-
-$app_folders = Get-ChildItem -Directory -Path $discord_root |
- Select-String -Pattern "app-"
-
-foreach ($folder in $app_folders)
-{
- $version = [regex]::match($folder, 'app-([\d\.]+)').Groups[1].Value
- Write-Output "Unpatching $branch Version $version"
-
- $resources = "$folder\resources"
- if (-not(Test-Path -Path "$resources")) {
- Write-Output "Resources folder doesn't exist... Possibly an outdated copy and can be ignored.`n"
- continue
- }
- if (-not(Test-Path -Path "$resources\app")) {
- Write-Output "App folder doesn't exist... Already unpatched?`n"
- continue
- }
-
- Remove-Item -Path "$folder\resources\app" -Recurse -Force -Confirm:$false
-
- if (Test-Path "$folder\resources\app")
- {
- Write-Error "Failed to delete $folder\resources\app"
- } else {
- Write-Output "Successfully unpatched $branch (version $version)"
- }
-
-} \ No newline at end of file
diff --git a/uninstall.sh b/uninstall.sh
deleted file mode 100755
index 4b4b422..0000000
--- a/uninstall.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-# Super simple uninstaller.
-# If this doesn't work for you, or you're not on Linux, just
-# manually delete the app folder in your Discord folder (inside resources)
-
-set -e
-
-discord="$(dirname "$(readlink "$(which discord)")")"
-rm -r --interactive=never "${discord:?Cant find discord}/resources/app"