Skip to content

Commit

Permalink
Merge pull request #11 from BetterDiscord/install-functions
Browse files Browse the repository at this point in the history
Add install functions and prepare for distribution
  • Loading branch information
zerebos authored Mar 29, 2021
2 parents 4d771c4 + 06578e0 commit 9108aad
Show file tree
Hide file tree
Showing 28 changed files with 747 additions and 147 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"DiscordNative": "readonly",
"__non_webpack_require__": "readonly",
"Symbol": "readonly",
"__static": "readonly"
"__static": "readonly",
"status": "off"
}
}
Binary file added assets/icon.icns
Binary file not shown.
Binary file added assets/icon.ico
Binary file not shown.
34 changes: 28 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "betterdiscord",
"productName": "BetterDiscord",
"description": "test",
"author": "Zerebos",
"description": "Installer for BetterDiscord.",
"author": "BetterDiscord",
"version": "1.0.0",
"license": "MIT",
"scripts": {
Expand All @@ -13,16 +13,17 @@
"dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null"
},
"dependencies": {
"bent": "^7.3.12",
"source-map-support": "^0.5.16"
},
"devDependencies": {
"del": "^6.0.0",
"electron": "9.4.0",
"electron-builder": "^22.4.1",
"electron-webpack": "^2.8.2",
"eslint": "^7.21.0",
"eslint-plugin-svelte3": "^3.1.2",
"find-process": "^1.4.4",
"phin": "^3.5.1",
"svelte": "^3.35.0",
"svelte-loader": "^3.0.0",
"svelte-spa-router": "^3.1.0",
Expand All @@ -32,11 +33,32 @@
"build": {
"appId": "app.betterdiscord.installer",
"productName": "BetterDiscord",
"copyright": "Copyright © 2021 BetterDiscord",
"afterAllArtifactBuild": "scripts/fixmac.js",
"win": {
"icon": "assets/icon.ico",
"target": {
"target": "portable",
"arch": ["ia32"]
}
},
"mac": {
"category": "your.app.category.type"
"icon": "assets/icon.icns",
"category": "public.app-category.social-networking",
"target": {
"target": "zip",
"arch": ["x64"]
}
},
"win": {
"target": "portable"
"linux": {
"category": "Utility",
"target": {
"target": "AppImage",
"arch": ["x64"]
}
},
"appImage": {
"license": "assets/license.txt"
}
},
"electronWebpack": {
Expand Down
49 changes: 49 additions & 0 deletions scripts/fixmac.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Exists due to https://github.com/electron-userland/electron-builder/issues/4299
// Tempfix adapted from: https://gist.github.com/harshitsilly/a1bd5a405f93966aad20358ae6c4cec5

const path = require("path");
const {execSync} = require("child_process");
const fs = require("fs");
const yaml = require("js-yaml");
const {appBuilderPath} = require("app-builder-bin");
const currentWorkingDirectory = process.cwd();
const packageInfo = require(path.join(currentWorkingDirectory, "package.json"));

const APP_NAME = packageInfo.build.productName;
const APP_VERSION = process.argv[2] ? process.argv[2] : packageInfo.version;
const APP_DIST_PATH = path.join(currentWorkingDirectory, "dist");


/* eslint-disable no-console */
module.exports = function(buildResult) {
if (!buildResult.artifactPaths.some(p => p.endsWith("mac.zip"))) return console.log("No Mac build detected");
console.log("Zipping Started");

execSync(
`ditto -c -k --sequesterRsrc --keepParent --zlibCompressionLevel 9 "${APP_DIST_PATH}/mac/${APP_NAME}.app" "${APP_DIST_PATH}/${APP_NAME}-${APP_VERSION}-mac.zip"`
);

console.log("Zipping Completed");

const APP_GENERATED_BINARY_PATH = path.join(APP_DIST_PATH, `${APP_NAME}-${APP_VERSION}-mac.zip`);
try {
const output = execSync(
`${appBuilderPath} blockmap --input="${APP_GENERATED_BINARY_PATH}" --output="${APP_DIST_PATH}/${APP_NAME}-${APP_VERSION}-mac.zip.blockmap" --compression=gzip`
);
const {sha512, size} = JSON.parse(output);

const ymlPath = path.join(APP_DIST_PATH, "latest-mac.yml");
const ymlData = yaml.safeLoad(fs.readFileSync(ymlPath, "utf8"));
// console.log(ymlData);
ymlData.sha512 = sha512;
ymlData.files[0].sha512 = sha512;
ymlData.files[0].size = size;
const yamlStr = yaml.safeDump(ymlData);
// console.log(yamlStr);
fs.writeFileSync(ymlPath, yamlStr, "utf8");
console.log("Successfully updated YAML file and configurations with blockmap.");
}
catch (e) {
console.log("Error in updating YAML file and configurations with blockmap.", e);
}
};
2 changes: 1 addition & 1 deletion src/renderer/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
left: 0;
width: 100%;
height: 100%;
background-image: url('images/background.png');
background-image: var(--background);
background-size: 60px;
background-repeat: repeat;
background-position: center;
Expand Down
191 changes: 95 additions & 96 deletions src/renderer/actions/install.js
Original file line number Diff line number Diff line change
@@ -1,138 +1,137 @@
import logs from "../stores/logs";
import {progress, status} from "../stores/installation";
import {remote, shell} from "electron";
import fs from "fs";
import {promises as fs} from "fs";
import path from "path";
import bent from "bent";
import kill from "tree-kill";
import findProcess from "find-process";

const downloadFile = bent("buffer");

function log(entry) {
logs.update(a => {
a.push(entry);
return a;
});
}

let progressCache = 0;

function addProgress(val) {
progressCache += val;
progress.set(progressCache);
}

function failInstallation() {
const discordURL = "https://discord.gg/0Tmfo5ZbORCRqbAd";
log("");
log(`The installation seems to have failed. If this problem is recurring, join our discord community for support. ${discordURL}`);
status.set("error");
}
import phin from "phin";

import {log, lognewline} from "./utils/log";
import succeed from "./utils/succeed";
import fail from "./utils/fail";
import exists from "./utils/exists";
import reset from "./utils/reset";
import kill from "./utils/kill";
import {showRestartNotice} from "./utils/notices";
import doSanityCheck from "./utils/sanity";

const MAKE_DIR_PROGRESS = 30;
const DOWNLOAD_PACKAGE_PROGRESS = 60;
const INJECT_SHIM_PROGRESS = 90;
const RESTART_DISCORD_PROGRESS = 100;

const bdFolder = path.join(remote.app.getPath("appData"), "BetterDiscord");
const bdDataFolder = path.join(bdFolder, "data");
const bdPluginsFolder = path.join(bdFolder, "plugins");
const bdThemesFolder = path.join(bdFolder, "themes");

async function makeDirectories() {
const folders = [bdFolder, bdDataFolder, bdThemesFolder, bdPluginsFolder];
async function makeDirectories(...folders) {
const progressPerLoop = (MAKE_DIR_PROGRESS - progress.value) / folders.length;
for (const folder of folders) {
if (fs.existsSync(folder)) {
if (await exists(folder)) {
log(`✅ Directory exists: ${folder}`);
progress.set(progress.value + progressPerLoop);
continue;
}
try {
fs.mkdirSync(folder);
await fs.mkdir(folder);
progress.set(progress.value + progressPerLoop);
log(`✅ Directory created: ${folder}`);
}
catch {
catch (err) {
log(`❌ Failed to create directory: ${folder}`);
failInstallation();
return;
log(`❌ ${err.message}`);
return err;
}
}
progress.set(25);
}

const downloadUrl = `https://bd.zerebos.com/betterdiscord.asar`;
const getJSON = phin.defaults({method: "GET", parse: "json", headers: {"User-Agent": "BetterDiscord Installer"}});
const downloadFile = phin.defaults({method: "GET", followRedirects: true, headers: {"User-Agent": "BetterDiscord Installer", "Accept": "application/octet-stream"}});
const asarPath = path.join(bdDataFolder, "betterdiscord.asar");
async function downloadAsar() {
const buffer = await downloadFile(downloadUrl);
const originalFs = require("original-fs"); // because electron doesn't like when I write asar files
originalFs.writeFileSync(asarPath, buffer);
}

async function restartDiscord() {
const results = await findProcess("name", "Discord", true);
if (!results || !results.length) return;
const parentPids = results.map(p => p.ppid);
const discordPid = results.find(p => parentPids.includes(p.pid));
const bin = discordPid.bin;
kill(discordPid.pid);
shell.openExternal(bin);
}


export default async function(discordPaths) {
progress.set(0);

log("Starting installation...");
log("");
log("Locating Discord paths...");

if (!discordPaths || !discordPaths.length) {
log("❌ Failed to locate required directories.");
failInstallation();
return;
}

let downloadUrl = "https://api.github.com/repos/rauenzi/BetterDiscordApp/releases";
try {
await makeDirectories();
const response = await getJSON(downloadUrl);
const releases = response.body;
const asset = releases[0].assets.find(a => a.name === "betterdiscord.asar");
downloadUrl = asset.url;

const resp = await downloadFile(downloadUrl);
const originalFs = require("original-fs").promises; // because electron doesn't like when I write asar files
await originalFs.writeFile(asarPath, resp.body);
}
catch (err) {
log(`❌ Failed to create directories - ${err.message}`);
failInstallation();
return;
}


log("");
log(`Downloading asar file from: ${downloadUrl}`);

try {
await downloadAsar();
log("✅ Package downloaded");
progress.set(50);
}
catch (err) {
log(`❌ Failed to download package - ${err.message}`);
failInstallation();
return;
log(`❌ Failed to download package ${downloadUrl}`);
log(`❌ ${err.message}`);
return err;
}
}

log("");
log("Injecting shims...");
for (const discordPath of discordPaths) {
async function injectShims(paths) {
const progressPerLoop = (INJECT_SHIM_PROGRESS - progress.value) / paths.length;
for (const discordPath of paths) {
log("Injecting into: " + discordPath);
const appPath = path.join(discordPath, "app");
const pkgFile = path.join(appPath, "package.json");
const indexFile = path.join(appPath, "index.js");
try {
if (!fs.existsSync(appPath)) fs.mkdirSync(appPath);
fs.writeFileSync(pkgFile, JSON.stringify({name: "betterdiscord", main: "index.js"}));
fs.writeFileSync(indexFile, `require("${asarPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}");`);
if (process.platform === "win32" || process.platform === "darwin") {
if (!(await exists(appPath))) await fs.mkdir(appPath);
await fs.writeFile(pkgFile, JSON.stringify({name: "betterdiscord", main: "index.js"}));
await fs.writeFile(indexFile, `require("${asarPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}");`);
}
else {
await fs.writeFile(path.join(discordPath, "index.js"), `require("${asarPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}");\nmodule.exports = require("./core.asar");`);
}
log("✅ Injection successful");
progress.set(progress.value + progressPerLoop);
}
catch (err) {
log(`❌ Injection Error - ${err.message}`);
failInstallation();
return;
log(`❌ Could not inject shims to ${discordPath}`);
log(`❌ ${err.message}`);
return err;
}
}
}


export default async function(config) {
await reset();
const sane = doSanityCheck(config);
if (!sane) return fail();


const channels = Object.keys(config);
const paths = Object.values(config);


lognewline("Creating required directories...");
const makeDirErr = await makeDirectories(bdFolder, bdDataFolder, bdThemesFolder, bdPluginsFolder);
if (makeDirErr) return fail();
log("✅ Directories created");
progress.set(MAKE_DIR_PROGRESS);


lognewline("Downloading asar file");
const downloadErr = await downloadAsar();
if (downloadErr) return fail();
log("✅ Package downloaded");
progress.set(DOWNLOAD_PACKAGE_PROGRESS);


lognewline("Injecting shims...");
const injectErr = await injectShims(paths);
if (injectErr) return fail();
log("✅ Shims injected");
progress.set(INJECT_SHIM_PROGRESS);


lognewline("Restarting Discord...");
const killErr = await kill(channels, (RESTART_DISCORD_PROGRESS - progress.value) / channels.length);
if (killErr) showRestartNotice(); // No need to bail out and show failed
else log("✅ Discord restarted");
progress.set(RESTART_DISCORD_PROGRESS);


log("Installation completed!");
progress.set(100);
status.set("success");
succeed();
};
Loading

0 comments on commit 9108aad

Please sign in to comment.