Vencord Standalone without git/node (#148)

This commit is contained in:
Ven 2022-10-23 23:23:52 +02:00 committed by GitHub
parent ffbb52512c
commit 5fac8be0ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 373 additions and 129 deletions

@ -2,7 +2,7 @@
"root": true, "root": true,
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"ignorePatterns": ["dist", "browser"], "ignorePatterns": ["dist", "browser"],
"plugins": ["header", "simple-import-sort"], "plugins": ["header", "simple-import-sort", "unused-imports"],
"rules": { "rules": {
// Since it's only been a month and Vencord has already been stolen // Since it's only been a month and Vencord has already been stolen
// by random skids who rebranded it to "AlphaCord" and erased all license // by random skids who rebranded it to "AlphaCord" and erased all license
@ -86,6 +86,8 @@
"prefer-spread": "error", "prefer-spread": "error",
"simple-import-sort/imports": "error", "simple-import-sort/imports": "error",
"simple-import-sort/exports": "error" "simple-import-sort/exports": "error",
"unused-imports/no-unused-imports": "error"
} }
} }

@ -28,7 +28,7 @@ jobs:
run: pnpm buildWeb run: pnpm buildWeb
- name: Build - name: Build
run: pnpm build run: pnpm build --standalone
- name: Get some values needed for the release - name: Get some values needed for the release
id: vars id: vars

@ -42,6 +42,7 @@
"eslint": "^8.24.0", "eslint": "^8.24.0",
"eslint-plugin-header": "^3.1.1", "eslint-plugin-header": "^3.1.1",
"eslint-plugin-simple-import-sort": "^8.0.0", "eslint-plugin-simple-import-sort": "^8.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"standalone-electron-types": "^1.0.0", "standalone-electron-types": "^1.0.0",
"type-fest": "^3.1.0", "type-fest": "^3.1.0",
"typescript": "^4.8.4" "typescript": "^4.8.4"

21
pnpm-lock.yaml generated

@ -11,6 +11,7 @@ specifiers:
eslint: ^8.24.0 eslint: ^8.24.0
eslint-plugin-header: ^3.1.1 eslint-plugin-header: ^3.1.1
eslint-plugin-simple-import-sort: ^8.0.0 eslint-plugin-simple-import-sort: ^8.0.0
eslint-plugin-unused-imports: ^2.0.0
fflate: ^0.7.4 fflate: ^0.7.4
standalone-electron-types: ^1.0.0 standalone-electron-types: ^1.0.0
type-fest: ^3.1.0 type-fest: ^3.1.0
@ -30,6 +31,7 @@ devDependencies:
eslint: 8.24.0 eslint: 8.24.0
eslint-plugin-header: 3.1.1_eslint@8.24.0 eslint-plugin-header: 3.1.1_eslint@8.24.0
eslint-plugin-simple-import-sort: 8.0.0_eslint@8.24.0 eslint-plugin-simple-import-sort: 8.0.0_eslint@8.24.0
eslint-plugin-unused-imports: 2.0.0_eslint@8.24.0
standalone-electron-types: 1.0.0 standalone-electron-types: 1.0.0
type-fest: 3.1.0 type-fest: 3.1.0
typescript: 4.8.4 typescript: 4.8.4
@ -582,6 +584,25 @@ packages:
eslint: 8.24.0 eslint: 8.24.0
dev: true dev: true
/eslint-plugin-unused-imports/2.0.0_eslint@8.24.0:
resolution: {integrity: sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
'@typescript-eslint/eslint-plugin': ^5.0.0
eslint: ^8.0.0
peerDependenciesMeta:
'@typescript-eslint/eslint-plugin':
optional: true
dependencies:
eslint: 8.24.0
eslint-rule-composer: 0.3.0
dev: true
/eslint-rule-composer/0.3.0:
resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==}
engines: {node: '>=4.0.0'}
dev: true
/eslint-scope/7.1.1: /eslint-scope/7.1.1:
resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}

@ -18,7 +18,22 @@
*/ */
import esbuild from "esbuild"; import esbuild from "esbuild";
import { commonOpts, fileIncludePlugin, gitHashPlugin, globPlugins, makeAllPackagesExternalPlugin } from "./common.mjs";
import { commonOpts, gitHash, globPlugins, isStandalone } from "./common.mjs";
const defines = {
IS_STANDALONE: isStandalone
};
if (defines.IS_STANDALONE === "false")
// If this is a local build (not standalone), optimise
// for the specific platform we're on
defines["process.platform"] = JSON.stringify(process.platform);
const header = `
// Vencord ${gitHash}
// Standalone: ${defines.IS_STANDALONE}
// Platform: ${defines["process.platform"] || "Universal"}
`.trim();
/** /**
* @type {esbuild.BuildOptions} * @type {esbuild.BuildOptions}
@ -30,8 +45,11 @@ const nodeCommonOpts = {
target: ["esnext"], target: ["esnext"],
minify: true, minify: true,
bundle: true, bundle: true,
sourcemap: "linked", external: ["electron", ...commonOpts.external],
external: ["electron"] define: defines,
banner: {
js: header
}
}; };
await Promise.all([ await Promise.all([
@ -39,11 +57,15 @@ await Promise.all([
...nodeCommonOpts, ...nodeCommonOpts,
entryPoints: ["src/preload.ts"], entryPoints: ["src/preload.ts"],
outfile: "dist/preload.js", outfile: "dist/preload.js",
footer: { js: "//# sourceURL=VencordPreload\n//# sourceMappingURL=vencord://preload.js.map" },
sourcemap: "external",
}), }),
esbuild.build({ esbuild.build({
...nodeCommonOpts, ...nodeCommonOpts,
entryPoints: ["src/patcher.ts"], entryPoints: ["src/patcher.ts"],
outfile: "dist/patcher.js", outfile: "dist/patcher.js",
footer: { js: "//# sourceURL=VencordPatcher\n//# sourceMappingURL=vencord://patcher.js.map" },
sourcemap: "external",
}), }),
esbuild.build({ esbuild.build({
...commonOpts, ...commonOpts,
@ -51,16 +73,16 @@ await Promise.all([
outfile: "dist/renderer.js", outfile: "dist/renderer.js",
format: "iife", format: "iife",
target: ["esnext"], target: ["esnext"],
footer: { js: "//# sourceURL=VencordRenderer" }, footer: { js: "//# sourceURL=VencordRenderer\n//# sourceMappingURL=vencord://renderer.js.map" },
globalName: "Vencord", globalName: "Vencord",
external: ["plugins", "git-hash"], sourcemap: "external",
plugins: [ plugins: [
globPlugins, globPlugins,
gitHashPlugin, ...commonOpts.plugins
fileIncludePlugin
], ],
define: { define: {
IS_WEB: "false" IS_WEB: "false",
IS_STANDALONE: isStandalone
} }
}), }),
]).catch(err => { ]).catch(err => {

@ -26,7 +26,7 @@ import { join } from "path";
// wtf is this assert syntax // wtf is this assert syntax
import PackageJSON from "../../package.json" assert { type: "json" }; import PackageJSON from "../../package.json" assert { type: "json" };
import { commonOpts, fileIncludePlugin, gitHashPlugin, globPlugins } from "./common.mjs"; import { commonOpts, fileIncludePlugin, gitHashPlugin, gitRemotePlugin, globPlugins } from "./common.mjs";
/** /**
* @type {esbuild.BuildOptions} * @type {esbuild.BuildOptions}
@ -40,11 +40,13 @@ const commonOptions = {
plugins: [ plugins: [
globPlugins, globPlugins,
gitHashPlugin, gitHashPlugin,
gitRemotePlugin,
fileIncludePlugin fileIncludePlugin
], ],
target: ["esnext"], target: ["esnext"],
define: { define: {
IS_WEB: "true" IS_WEB: "true",
IS_STANDALONE: "true"
} }
}; };

@ -16,13 +16,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { execSync } from "child_process"; import { exec, execSync } from "child_process";
import esbuild from "esbuild"; import esbuild from "esbuild";
import { existsSync } from "fs"; import { existsSync } from "fs";
import { readdir, readFile } from "fs/promises"; import { readdir, readFile } from "fs/promises";
import { join } from "path"; import { join } from "path";
import { promisify } from "util";
const watch = process.argv.includes("--watch"); export const watch = process.argv.includes("--watch");
export const isStandalone = JSON.stringify(process.argv.includes("--standalone"));
// https://github.com/evanw/esbuild/issues/619#issuecomment-751995294 // https://github.com/evanw/esbuild/issues/619#issuecomment-751995294
/** /**
@ -42,14 +44,15 @@ export const makeAllPackagesExternalPlugin = {
export const globPlugins = { export const globPlugins = {
name: "glob-plugins", name: "glob-plugins",
setup: build => { setup: build => {
build.onResolve({ filter: /^plugins$/ }, args => { const filter = /^~plugins$/;
build.onResolve({ filter }, args => {
return { return {
namespace: "import-plugins", namespace: "import-plugins",
path: args.path path: args.path
}; };
}); });
build.onLoad({ filter: /^plugins$/, namespace: "import-plugins" }, async () => { build.onLoad({ filter, namespace: "import-plugins" }, async () => {
const pluginDirs = ["plugins", "userplugins"]; const pluginDirs = ["plugins", "userplugins"];
let code = ""; let code = "";
let plugins = "\n"; let plugins = "\n";
@ -76,14 +79,14 @@ export const globPlugins = {
} }
}; };
const gitHash = execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim(); export const gitHash = execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
/** /**
* @type {esbuild.Plugin} * @type {esbuild.Plugin}
*/ */
export const gitHashPlugin = { export const gitHashPlugin = {
name: "git-hash-plugin", name: "git-hash-plugin",
setup: build => { setup: build => {
const filter = /^git-hash$/; const filter = /^~git-hash$/;
build.onResolve({ filter }, args => ({ build.onResolve({ filter }, args => ({
namespace: "git-hash", path: args.path namespace: "git-hash", path: args.path
})); }));
@ -93,13 +96,35 @@ export const gitHashPlugin = {
} }
}; };
/**
* @type {esbuild.Plugin}
*/
export const gitRemotePlugin = {
name: "git-remote-plugin",
setup: build => {
const filter = /^~git-remote$/;
build.onResolve({ filter }, args => ({
namespace: "git-remote", path: args.path
}));
build.onLoad({ filter, namespace: "git-remote" }, async () => {
const res = await promisify(exec)("git remote get-url origin", { encoding: "utf-8" });
const remote = res.stdout.trim()
.replace("https://github.com/", "")
.replace("git@github.com:", "")
.replace(/.git$/, "");
return { contents: `export default "${remote}"` };
});
}
};
/** /**
* @type {esbuild.Plugin} * @type {esbuild.Plugin}
*/ */
export const fileIncludePlugin = { export const fileIncludePlugin = {
name: "file-include-plugin", name: "file-include-plugin",
setup: build => { setup: build => {
const filter = /^@fileContent\/.+$/; const filter = /^~fileContent\/.+$/;
build.onResolve({ filter }, args => ({ build.onResolve({ filter }, args => ({
namespace: "include-file", namespace: "include-file",
path: args.path, path: args.path,
@ -126,5 +151,6 @@ export const commonOpts = {
minify: !watch, minify: !watch,
sourcemap: watch ? "inline" : "", sourcemap: watch ? "inline" : "",
legalComments: "linked", legalComments: "linked",
plugins: [fileIncludePlugin] plugins: [fileIncludePlugin, gitHashPlugin, gitRemotePlugin],
external: ["~plugins", "~git-hash", "~git-remote"]
}; };

@ -18,7 +18,7 @@
import { makeCodeblock } from "../../utils/misc"; import { makeCodeblock } from "../../utils/misc";
import { generateId, sendBotMessage } from "./commandHelpers"; import { generateId, sendBotMessage } from "./commandHelpers";
import { ApplicationCommandInputType, ApplicationCommandType, Argument, Command, CommandContext, CommandReturnValue,Option } from "./types"; import { ApplicationCommandInputType, ApplicationCommandType, Argument, Command, CommandContext, Option } from "./types";
export * from "./commandHelpers"; export * from "./commandHelpers";
export * from "./types"; export * from "./types";

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import plugins from "plugins"; import plugins from "~plugins";
import IpcEvents from "../utils/IpcEvents"; import IpcEvents from "../utils/IpcEvents";
import { mergeDefaults } from "../utils/misc"; import { mergeDefaults } from "../utils/misc";

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import monacoHtml from "@fileContent/monacoWin.html"; import monacoHtml from "~fileContent/monacoWin.html";
import { IpcEvents } from "../utils"; import { IpcEvents } from "../utils";
import { debounce } from "../utils/debounce"; import { debounce } from "../utils/debounce";

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import Plugins from "plugins"; import Plugins from "~plugins";
import { showNotice } from "../../api/Notices"; import { showNotice } from "../../api/Notices";
import { Settings, useSettings } from "../../api/settings"; import { Settings, useSettings } from "../../api/settings";

@ -50,7 +50,6 @@ export default ErrorBoundary.wrap(function Settings() {
})); }));
}, []); }, []);
return ( return (
<Forms.FormSection tag="h1" title="Vencord"> <Forms.FormSection tag="h1" title="Vencord">
<Forms.FormTitle tag="h5"> <Forms.FormTitle tag="h5">

@ -16,10 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import gitHash from "git-hash"; import gitHash from "~git-hash";
import { classes, useAwaiter } from "../utils/misc"; import { classes, useAwaiter } from "../utils/misc";
import { changes, checkForUpdates, getRepo, isNewer,isOutdated, rebuild, update, updateError, UpdateLogger } from "../utils/updater"; import { changes, checkForUpdates, getRepo, isNewer, rebuild, update, updateError, UpdateLogger } from "../utils/updater";
import { Alerts, Button, Card, Forms, Margins, Parser, React, Toasts } from "../webpack/common"; import { Alerts, Button, Card, Forms, Margins, Parser, React, Toasts } from "../webpack/common";
import ErrorBoundary from "./ErrorBoundary"; import ErrorBoundary from "./ErrorBoundary";
import { ErrorCard } from "./ErrorCard"; import { ErrorCard } from "./ErrorCard";

2
src/globals.d.ts vendored

@ -31,6 +31,8 @@ declare global {
* replace: `${IS_WEB}?foo:bar` * replace: `${IS_WEB}?foo:bar`
*/ */
export var IS_WEB: boolean; export var IS_WEB: boolean;
export var IS_STANDALONE: boolean;
export var VencordNative: typeof import("./VencordNative").default; export var VencordNative: typeof import("./VencordNative").default;
export var Vencord: typeof import("./Vencord"); export var Vencord: typeof import("./Vencord");
export var appSettings: { export var appSettings: {

@ -19,33 +19,15 @@
import { session } from "electron"; import { session } from "electron";
import { unzip } from "fflate"; import { unzip } from "fflate";
import { constants as fsConstants } from "fs"; import { constants as fsConstants } from "fs";
import { access,mkdir, rm, writeFile } from "fs/promises"; import { access, mkdir, rm, writeFile } from "fs/promises";
import https from "https";
import { join } from "path"; import { join } from "path";
import { DATA_DIR } from "./constants"; import { DATA_DIR } from "./constants";
import { crxToZip } from "./crxToZip"; import { crxToZip } from "./crxToZip";
import { get } from "./simpleGet";
const extensionCacheDir = join(DATA_DIR, "ExtensionCache"); const extensionCacheDir = join(DATA_DIR, "ExtensionCache");
function download(url: string) {
return new Promise<Buffer>((resolve, reject) => {
https.get(url, res => {
const { statusCode, statusMessage, headers } = res;
if (statusCode! >= 400)
return void reject(`${statusCode}: ${statusMessage} - ${url}`);
if (statusCode! >= 300)
return void resolve(download(headers.location!));
const chunks = [] as Buffer[];
res.on("error", reject);
res.on("data", chunk => chunks.push(chunk));
res.once("end", () => resolve(Buffer.concat(chunks)));
});
});
}
async function extract(data: Buffer, outDir: string) { async function extract(data: Buffer, outDir: string) {
await mkdir(outDir, { recursive: true }); await mkdir(outDir, { recursive: true });
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
@ -86,7 +68,7 @@ export async function installExt(id: string) {
await access(extDir, fsConstants.F_OK); await access(extDir, fsConstants.F_OK);
} catch (err) { } catch (err) {
const url = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${id}%26uc&prodversion=32`; const url = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${id}%26uc&prodversion=32`;
const buf = await download(url); const buf = await get(url);
await extract(crxToZip(buf), extDir); await extract(crxToZip(buf), extDir);
} }

@ -18,19 +18,18 @@
import "./updater"; import "./updater";
import monacoHtml from "@fileContent/../components/monacoWin.html;base64"; import { BrowserWindow, desktopCapturer, ipcMain, shell } from "electron";
import { app, BrowserWindow, desktopCapturer, ipcMain, shell } from "electron";
import { mkdirSync, readFileSync, watch } from "fs"; import { mkdirSync, readFileSync, watch } from "fs";
import { open, readFile, writeFile } from "fs/promises"; import { open, readFile, writeFile } from "fs/promises";
import { join } from "path"; import { join } from "path";
import monacoHtml from "~fileContent/../components/monacoWin.html;base64";
import { debounce } from "../utils/debounce"; import { debounce } from "../utils/debounce";
import IpcEvents from "../utils/IpcEvents"; import IpcEvents from "../utils/IpcEvents";
import { Queue } from "../utils/Queue"; import { Queue } from "../utils/Queue";
import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, SETTINGS_DIR, SETTINGS_FILE } from "./constants"; import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, SETTINGS_DIR, SETTINGS_FILE } from "./constants";
mkdirSync(SETTINGS_DIR, { recursive: true }); mkdirSync(SETTINGS_DIR, { recursive: true });
function readCss() { function readCss() {

37
src/ipcMain/simpleGet.ts Normal file

@ -0,0 +1,37 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import https from "https";
export function get(url: string, options: https.RequestOptions = {}) {
return new Promise<Buffer>((resolve, reject) => {
https.get(url, options, res => {
const { statusCode, statusMessage, headers } = res;
if (statusCode! >= 400)
return void reject(`${statusCode}: ${statusMessage} - ${url}`);
if (statusCode! >= 300)
return void resolve(get(headers.location!, options));
const chunks = [] as Buffer[];
res.on("error", reject);
res.on("data", chunk => chunks.push(chunk));
res.once("end", () => resolve(Buffer.concat(chunks)));
});
});
}

@ -0,0 +1,59 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { createHash } from "crypto";
import { createReadStream } from "fs";
import { join } from "path";
export async function calculateHashes() {
const hashes = {} as Record<string, string>;
await Promise.all(
["patcher.js", "preload.js", "renderer.js"].map(file => new Promise<void>(r => {
const fis = createReadStream(join(__dirname, file));
const hash = createHash("sha1", { encoding: "hex" });
fis.once("end", () => {
hash.end();
hashes[file] = hash.read();
r();
});
fis.pipe(hash);
}))
);
return hashes;
}
export function serializeErrors(func: (...args: any[]) => any) {
return async function () {
try {
return {
ok: true,
value: await func(...arguments)
};
} catch (e: any) {
return {
ok: false,
error: e instanceof Error ? {
// prototypes get lost, so turn error into plain object
...e
} : e
};
}
};
}

@ -17,13 +17,12 @@
*/ */
import { execFile as cpExecFile } from "child_process"; import { execFile as cpExecFile } from "child_process";
import { createHash } from "crypto";
import { ipcMain } from "electron"; import { ipcMain } from "electron";
import { createReadStream } from "fs";
import { join } from "path"; import { join } from "path";
import { promisify } from "util"; import { promisify } from "util";
import IpcEvents from "../utils/IpcEvents"; import IpcEvents from "../../utils/IpcEvents";
import { calculateHashes, serializeErrors } from "./common";
const VENCORD_SRC_DIR = join(__dirname, ".."); const VENCORD_SRC_DIR = join(__dirname, "..");
@ -35,44 +34,6 @@ function git(...args: string[]) {
}); });
} }
async function calculateHashes() {
const hashes = {} as Record<string, string>;
await Promise.all(
["patcher.js", "preload.js", "renderer.js"].map(file => new Promise<void>(r => {
const fis = createReadStream(join(__dirname, file));
const hash = createHash("sha1", { encoding: "hex" });
fis.once("end", () => {
hash.end();
hashes[file] = hash.read();
r();
});
fis.pipe(hash);
}))
);
return hashes;
}
function serializeErrors(func: (...args: any[]) => any) {
return async function () {
try {
return {
ok: true,
value: await func(...arguments)
};
} catch (e: any) {
return {
ok: false,
error: e instanceof Error ? {
// prototypes get lost, so turn error into plain object
...e
} : e
};
}
};
}
async function getRepo() { async function getRepo() {
const res = await git("remote", "get-url", "origin"); const res = await git("remote", "get-url", "origin");
return res.stdout.trim() return res.stdout.trim()

@ -0,0 +1,86 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { ipcMain } from "electron";
import { writeFile } from "fs/promises";
import { join } from "path";
import gitHash from "~git-hash";
import gitRemote from "~git-remote";
import { VENCORD_USER_AGENT } from "../../utils/constants";
import IpcEvents from "../../utils/IpcEvents";
import { get } from "../simpleGet";
import { calculateHashes, serializeErrors } from "./common";
const API_BASE = `https://api.github.com/repos/${gitRemote}`;
let PendingUpdates = [] as [string, Buffer][];
async function githubGet(endpoint: string) {
return get(API_BASE + endpoint, {
headers: {
Accept: "application/vnd.github+json",
// "All API requests MUST include a valid User-Agent header.
// Requests with no User-Agent header will be rejected."
"User-Agent": VENCORD_USER_AGENT,
// todo: perhaps add support for (optional) api token?
// unauthorised rate limit is 60 reqs/h
// https://github.com/settings/tokens/new?description=Vencord%20Updater
}
});
}
async function calculateGitChanges() {
const res = await githubGet(`/compare/${gitHash}...HEAD`);
const data = JSON.parse(res.toString("utf-8"));
return data.commits.map(c => ({
// github api only sends the long sha
hash: c.sha.slice(0, 7),
author: c.author.login,
message: c.commit.message
}));
}
async function fetchUpdates() {
const release = await githubGet("/releases/latest");
const data = JSON.parse(release.toString());
const hash = data.name.slice(data.name.lastIndexOf(" ") + 1);
if (hash === gitHash)
return true;
await Promise.all(data.assets.map(async ({ name, browser_download_url }) => {
if (["patcher.js", "preload.js", "renderer.js"].some(s => name.startsWith(s))) {
PendingUpdates.push([name, await get(browser_download_url)]);
}
}));
return true;
}
async function applyUpdates() {
await Promise.all(PendingUpdates.map(([name, data]) => writeFile(join(__dirname, name), data)));
PendingUpdates = [];
return true;
}
ipcMain.handle(IpcEvents.GET_HASHES, serializeErrors(calculateHashes));
ipcMain.handle(IpcEvents.GET_REPO, serializeErrors(() => `https://github.com/${gitRemote}`));
ipcMain.handle(IpcEvents.GET_UPDATES, serializeErrors(calculateGitChanges));
ipcMain.handle(IpcEvents.UPDATE, serializeErrors(fetchUpdates));
ipcMain.handle(IpcEvents.BUILD, serializeErrors(applyUpdates));

@ -0,0 +1,19 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import(IS_STANDALONE ? "./http" : "./git");

10
src/modules.d.ts vendored

@ -19,17 +19,21 @@
// eslint-disable-next-line spaced-comment // eslint-disable-next-line spaced-comment
/// <reference types="standalone-electron-types"/> /// <reference types="standalone-electron-types"/>
declare module "plugins" { declare module "~plugins" {
const plugins: Record<string, import("./utils/types").Plugin>; const plugins: Record<string, import("./utils/types").Plugin>;
export default plugins; export default plugins;
} }
declare module "git-hash" { declare module "~git-hash" {
const hash: string; const hash: string;
export default hash; export default hash;
} }
declare module "~git-remote" {
const remote: string;
export default remote;
}
declare module "@fileContent/*" { declare module "~fileContent/*" {
const content: string; const content: string;
export default content; export default content;
} }

@ -87,6 +87,22 @@ Object.defineProperty(global, "appSettings", {
process.env.DATA_DIR = join(app.getPath("userData"), "..", "Vencord"); process.env.DATA_DIR = join(app.getPath("userData"), "..", "Vencord");
electron.app.whenReady().then(() => { electron.app.whenReady().then(() => {
// Source Maps! Maybe there's a better way but since the renderer is executed
// from a string I don't think any other form of sourcemaps would work
electron.protocol.registerFileProtocol("vencord", ({ url: unsafeUrl }, cb) => {
let url = unsafeUrl.slice("vencord://".length);
if (url.endsWith("/")) url = url.slice(0, -1);
switch (url) {
case "renderer.js.map":
case "preload.js.map":
case "patcher.js.map": // doubt
cb(join(__dirname, url));
break;
default:
cb({ statusCode: 403 });
}
});
try { try {
const settings = JSON.parse(readSettings()); const settings = JSON.parse(readSettings());
if (settings.enableReactDevtools) if (settings.enableReactDevtools)

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import Plugins from "plugins"; import Plugins from "~plugins";
import { registerCommand, unregisterCommand } from "../api/Commands"; import { registerCommand, unregisterCommand } from "../api/Commands";
import { Settings } from "../api/settings"; import { Settings } from "../api/settings";

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { classes,useAwaiter } from "../../../utils"; import { useAwaiter } from "../../../utils";
import { Settings } from "../../../Vencord"; import { Settings } from "../../../Vencord";
import { UserStore } from "../../../webpack/common"; import { UserStore } from "../../../webpack/common";
import { PronounMapping, UserProfileProps } from "../types"; import { PronounMapping, UserProfileProps } from "../types";

@ -16,9 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import gitHash from "git-hash"; import { VENCORD_USER_AGENT } from "../../utils/constants";
import { debounce } from "../../utils/debounce";
import { debounce } from "../../utils";
import { Settings } from "../../Vencord"; import { Settings } from "../../Vencord";
import { PronounsFormat } from "."; import { PronounsFormat } from ".";
import { PronounCode, PronounMapping, PronounsResponse } from "./types"; import { PronounCode, PronounMapping, PronounsResponse } from "./types";
@ -64,7 +63,7 @@ async function bulkFetchPronouns(ids: string[]): Promise<PronounsResponse> {
method: "GET", method: "GET",
headers: { headers: {
"Accept": "application/json", "Accept": "application/json",
"X-PronounDB-Source": `Vencord/${gitHash} (github.com/Vendicated/Vencord)` "X-PronounDB-Source": VENCORD_USER_AGENT
} }
}); });
return await req.json() return await req.json()

@ -16,9 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Message } from "discord-types/general";
import { PartialDeep } from "type-fest";
import { ApplicationCommandInputType, sendBotMessage } from "../api/Commands"; import { ApplicationCommandInputType, sendBotMessage } from "../api/Commands";
import { lazyWebpack } from "../utils"; import { lazyWebpack } from "../utils";
import { Devs } from "../utils/constants"; import { Devs } from "../utils/constants";
@ -27,33 +24,33 @@ import { filters } from "../webpack";
import { FluxDispatcher } from "../webpack/common"; import { FluxDispatcher } from "../webpack/common";
interface Album { interface Album {
id: string id: string;
image: { image: {
height: number height: number;
width: number width: number;
url: string url: string;
} };
name: string name: string;
} }
interface Artist { interface Artist {
external_urls: { external_urls: {
spotify: string spotify: string;
} };
href: string href: string;
id: string id: string;
name: string name: string;
type: "artist" | string type: "artist" | string;
uri: string uri: string;
} }
interface Track { interface Track {
id: string id: string;
album: Album album: Album;
artists: Artist[] artists: Artist[];
duration: number duration: number;
isLocal: boolean isLocal: boolean;
name: string name: string;
} }
const Spotify = lazyWebpack(filters.byProps(["getPlayerState"])); const Spotify = lazyWebpack(filters.byProps(["getPlayerState"]));

@ -1,6 +1,6 @@
/* /*
* Vencord, a modification for Discord's desktop app * Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Vendicated and contributors * Copyright (c) 2022 Vendicated and Megumin
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import gitHash from "git-hash"; import gitHash from "~git-hash";
import { Devs } from "../utils/constants"; import { Devs } from "../utils/constants";
import definePlugin from "../utils/types"; import definePlugin from "../utils/types";
@ -34,7 +34,13 @@ export default definePlugin({
replace: m => { replace: m => {
const idx = m.indexOf("Host") - 1; const idx = m.indexOf("Host") - 1;
const template = m.slice(0, idx); const template = m.slice(0, idx);
let r = `${m}, ${template}"Vencord ", "${gitHash}${IS_WEB ? " (Web)" : ""}"), " ")`; const additionalInfo = IS_WEB
? " (Web)"
: IS_STANDALONE
? " (Standalone)"
: "";
let r = `${m}, ${template}"Vencord ", "${gitHash}${additionalInfo}"), " ")`;
if (!IS_WEB) { if (!IS_WEB) {
r += `,${template} "Electron ",VencordNative.getVersions().electron)," "),`; r += `,${template} "Electron ",VencordNative.getVersions().electron)," "),`;
r += `${template} "Chrome ",VencordNative.getVersions().chrome)," ")`; r += `${template} "Chrome ",VencordNative.getVersions().chrome)," ")`;

@ -16,8 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import gitHash from "~git-hash";
import gitRemote from "~git-remote";
export const WEBPACK_CHUNK = "webpackChunkdiscord_app"; export const WEBPACK_CHUNK = "webpackChunkdiscord_app";
export const REACT_GLOBAL = "Vencord.Webpack.Common.React"; export const REACT_GLOBAL = "Vencord.Webpack.Common.React";
export const VENCORD_USER_AGENT = `Vencord/${gitHash}${gitRemote ? ` (https://github.com/${gitRemote})` : ""}`;
// Add yourself here if you made more than one plugin // Add yourself here if you made more than one plugin
export const Devs = Object.freeze({ export const Devs = Object.freeze({

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import gitHash from "git-hash"; import gitHash from "~git-hash";
import IpcEvents from "./IpcEvents"; import IpcEvents from "./IpcEvents";
import Logger from "./logger"; import Logger from "./logger";