1
0
Fork 1
mirror of https://github.com/Vendicated/Vencord.git synced 2025-01-26 17:26:22 +00:00

Merge branch 'main' into main

This commit is contained in:
Wolfie 2024-05-30 17:35:10 -04:00 committed by GitHub
commit a0076558c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 200 additions and 113 deletions

View file

@ -140,8 +140,14 @@ if (!IS_VANILLA) {
return originalAppend.apply(this, args); return originalAppend.apply(this, args);
}; };
// disable renderer backgrounding to prevent the app from unloading when in the background
// https://github.com/electron/electron/issues/2822
// https://github.com/GoogleChrome/chrome-launcher/blob/5a27dd574d47a75fec0fb50f7b774ebf8a9791ba/docs/chrome-flags-for-tools.md#task-throttling
// Work around discord unloading when in background // Work around discord unloading when in background
// Discord also recently started adding these flags but only on windows for some reason dunno why, it happens on Linux too
app.commandLine.appendSwitch("disable-renderer-backgrounding"); app.commandLine.appendSwitch("disable-renderer-backgrounding");
app.commandLine.appendSwitch("disable-background-timer-throttling");
app.commandLine.appendSwitch("disable-backgrounding-occluded-windows");
} else { } else {
console.log("[Vencord] Running in vanilla mode. Not loading Vencord"); console.log("[Vencord] Running in vanilla mode. Not loading Vencord");
} }

View file

@ -17,138 +17,198 @@
*/ */
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getCurrentChannel, getCurrentGuild } from "@utils/discord";
import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy";
import { relaunch } from "@utils/native"; import { relaunch } from "@utils/native";
import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches"; import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches";
import definePlugin, { StartAt } from "@utils/types"; import definePlugin, { PluginNative, StartAt } from "@utils/types";
import * as Webpack from "@webpack"; import * as Webpack from "@webpack";
import { extract, filters, findAll, findModuleId, search } from "@webpack"; import { extract, filters, findAll, findModuleId, search } from "@webpack";
import * as Common from "@webpack/common"; import * as Common from "@webpack/common";
import type { ComponentType } from "react"; import type { ComponentType } from "react";
const WEB_ONLY = (f: string) => () => { const DESKTOP_ONLY = (f: string) => () => {
throw new Error(`'${f}' is Discord Desktop only.`); throw new Error(`'${f}' is Discord Desktop only.`);
}; };
const define: typeof Object.defineProperty =
(obj, prop, desc) => {
if (Object.hasOwn(desc, "value"))
desc.writable = true;
return Object.defineProperty(obj, prop, {
configurable: true,
enumerable: true,
...desc
});
};
function makeShortcuts() {
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) {
const cache = new Map<string, unknown>();
return function (...filterProps: unknown[]) {
const cacheKey = String(filterProps);
if (cache.has(cacheKey)) return cache.get(cacheKey);
const matches = findAll(filterFactory(...filterProps));
const result = (() => {
switch (matches.length) {
case 0: return null;
case 1: return matches[0];
default:
const uniqueMatches = [...new Set(matches)];
if (uniqueMatches.length > 1)
console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches);
return matches[0];
}
})();
if (result && cacheKey) cache.set(cacheKey, result);
return result;
};
}
let fakeRenderWin: WeakRef<Window> | undefined;
const find = newFindWrapper(f => f);
const findByProps = newFindWrapper(filters.byProps);
return {
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
wp: Webpack,
wpc: { getter: () => Webpack.cache },
wreq: { getter: () => Webpack.wreq },
wpsearch: search,
wpex: extract,
wpexs: (code: string) => extract(findModuleId(code)!),
find,
findAll: findAll,
findByProps,
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
findByCode: newFindWrapper(filters.byCode),
findAllByCode: (code: string) => findAll(filters.byCode(code)),
findComponentByCode: newFindWrapper(filters.componentByCode),
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
findStore: newFindWrapper(filters.byStoreName),
PluginsApi: { getter: () => Vencord.Plugins },
plugins: { getter: () => Vencord.Plugins.plugins },
Settings: { getter: () => Vencord.Settings },
Api: { getter: () => Vencord.Api },
Util: { getter: () => Vencord.Util },
reload: () => location.reload(),
restart: IS_WEB ? DESKTOP_ONLY("restart") : relaunch,
canonicalizeMatch,
canonicalizeReplace,
canonicalizeReplacement,
fakeRender: (component: ComponentType, props: any) => {
const prevWin = fakeRenderWin?.deref();
const win = prevWin?.closed === false
? prevWin
: window.open("about:blank", "Fake Render", "popup,width=500,height=500")!;
fakeRenderWin = new WeakRef(win);
win.focus();
const doc = win.document;
doc.body.style.margin = "1em";
if (!win.prepared) {
win.prepared = true;
[...document.querySelectorAll("style"), ...document.querySelectorAll("link[rel=stylesheet]")].forEach(s => {
const n = s.cloneNode(true) as HTMLStyleElement | HTMLLinkElement;
if (s.parentElement?.tagName === "HEAD")
doc.head.append(n);
else if (n.id?.startsWith("vencord-") || n.id?.startsWith("vcd-"))
doc.documentElement.append(n);
else
doc.body.append(n);
});
}
Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
},
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
channel: { getter: () => getCurrentChannel(), preload: false },
channelId: { getter: () => Common.SelectedChannelStore.getChannelId(), preload: false },
guild: { getter: () => getCurrentGuild(), preload: false },
guildId: { getter: () => Common.SelectedGuildStore.getGuildId(), preload: false },
me: { getter: () => Common.UserStore.getCurrentUser(), preload: false },
meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false },
messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false }
};
}
function loadAndCacheShortcut(key: string, val: any, forceLoad: boolean) {
const currentVal = val.getter();
if (!currentVal || val.preload === false) return currentVal;
const value = currentVal[SYM_LAZY_GET]
? forceLoad ? currentVal[SYM_LAZY_GET]() : currentVal[SYM_LAZY_CACHED]
: currentVal;
if (value) define(window.shortcutList, key, { value });
return value;
}
export default definePlugin({ export default definePlugin({
name: "ConsoleShortcuts", name: "ConsoleShortcuts",
description: "Adds shorter Aliases for many things on the window. Run `shortcutList` for a list.", description: "Adds shorter Aliases for many things on the window. Run `shortcutList` for a list.",
authors: [Devs.Ven], authors: [Devs.Ven],
getShortcuts(): Record<PropertyKey, any> {
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) {
const cache = new Map<string, unknown>();
return function (...filterProps: unknown[]) {
const cacheKey = String(filterProps);
if (cache.has(cacheKey)) return cache.get(cacheKey);
const matches = findAll(filterFactory(...filterProps));
const result = (() => {
switch (matches.length) {
case 0: return null;
case 1: return matches[0];
default:
const uniqueMatches = [...new Set(matches)];
if (uniqueMatches.length > 1)
console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches);
return matches[0];
}
})();
if (result && cacheKey) cache.set(cacheKey, result);
return result;
};
}
let fakeRenderWin: WeakRef<Window> | undefined;
const find = newFindWrapper(f => f);
const findByProps = newFindWrapper(filters.byProps);
return {
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
wp: Webpack,
wpc: { getter: () => Webpack.cache },
wreq: { getter: () => Webpack.wreq },
wpsearch: search,
wpex: extract,
wpexs: (code: string) => extract(findModuleId(code)!),
find,
findAll: findAll,
findByProps,
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
findByCode: newFindWrapper(filters.byCode),
findAllByCode: (code: string) => findAll(filters.byCode(code)),
findComponentByCode: newFindWrapper(filters.componentByCode),
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
findStore: newFindWrapper(filters.byStoreName),
PluginsApi: { getter: () => Vencord.Plugins },
plugins: { getter: () => Vencord.Plugins.plugins },
Settings: { getter: () => Vencord.Settings },
Api: { getter: () => Vencord.Api },
reload: () => location.reload(),
restart: IS_WEB ? WEB_ONLY("restart") : relaunch,
canonicalizeMatch,
canonicalizeReplace,
canonicalizeReplacement,
fakeRender: (component: ComponentType, props: any) => {
const prevWin = fakeRenderWin?.deref();
const win = prevWin?.closed === false ? prevWin : window.open("about:blank", "Fake Render", "popup,width=500,height=500")!;
fakeRenderWin = new WeakRef(win);
win.focus();
const doc = win.document;
doc.body.style.margin = "1em";
if (!win.prepared) {
win.prepared = true;
[...document.querySelectorAll("style"), ...document.querySelectorAll("link[rel=stylesheet]")].forEach(s => {
const n = s.cloneNode(true) as HTMLStyleElement | HTMLLinkElement;
if (s.parentElement?.tagName === "HEAD")
doc.head.append(n);
else if (n.id?.startsWith("vencord-") || n.id?.startsWith("vcd-"))
doc.documentElement.append(n);
else
doc.body.append(n);
});
}
Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
}
};
},
startAt: StartAt.Init, startAt: StartAt.Init,
start() { start() {
const shortcuts = this.getShortcuts(); const shortcuts = makeShortcuts();
window.shortcutList = {}; window.shortcutList = {};
for (const [key, val] of Object.entries(shortcuts)) { for (const [key, val] of Object.entries(shortcuts)) {
if (val.getter != null) { if ("getter" in val) {
Object.defineProperty(window.shortcutList, key, { define(window.shortcutList, key, {
get: val.getter, get: () => loadAndCacheShortcut(key, val, true)
configurable: true,
enumerable: true
}); });
Object.defineProperty(window, key, { define(window, key, {
get: () => window.shortcutList[key], get: () => window.shortcutList[key]
configurable: true,
enumerable: true
}); });
} else { } else {
window.shortcutList[key] = val; window.shortcutList[key] = val;
window[key] = val; window[key] = val;
} }
} }
// unproxy loaded modules
Webpack.onceReady.then(() => {
setTimeout(() => this.eagerLoad(false), 1000);
if (!IS_WEB) {
const Native = VencordNative.pluginHelpers.ConsoleShortcuts as PluginNative<typeof import("./native")>;
Native.initDevtoolsOpenEagerLoad();
}
});
},
async eagerLoad(forceLoad: boolean) {
await Webpack.onceReady;
const shortcuts = makeShortcuts();
for (const [key, val] of Object.entries(shortcuts)) {
if (!Object.hasOwn(val, "getter") || (val as any).preload === false) continue;
try {
loadAndCacheShortcut(key, val, forceLoad);
} catch { } // swallow not found errors in DEV
}
}, },
stop() { stop() {
delete window.shortcutList; delete window.shortcutList;
for (const key in this.getShortcuts()) { for (const key in makeShortcuts()) {
delete window[key]; delete window[key];
} }
} }

View file

@ -0,0 +1,16 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { IpcMainInvokeEvent } from "electron";
export function initDevtoolsOpenEagerLoad(e: IpcMainInvokeEvent) {
const handleDevtoolsOpened = () => e.sender.executeJavaScript("Vencord.Plugins.plugins.ConsoleShortcuts.eagerLoad(true)");
if (e.sender.isDevToolsOpened())
handleDevtoolsOpened();
else
e.sender.once("devtools-opened", () => handleDevtoolsOpened());
}

View file

@ -2,6 +2,8 @@
Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations. Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations.
Read more about summaries in the [official Discord help article](https://support.discord.com/hc/en-us/articles/12926016807575-In-Channel-Conversation-Summaries)!
Note that this plugin can't fetch old summaries, it can only display ones created while your Discord is running with the plugin enabled. Note that this plugin can't fetch old summaries, it can only display ones created while your Discord is running with the plugin enabled.
![](https://github.com/Vendicated/Vencord/assets/45497981/bd931b0c-2e85-4c10-9f7c-8ba01eb55745) ![](https://github.com/Vendicated/Vencord/assets/45497981/bd931b0c-2e85-4c10-9f7c-8ba01eb55745)

View file

@ -35,8 +35,8 @@ const unconfigurable = ["arguments", "caller", "prototype"];
const handler: ProxyHandler<any> = {}; const handler: ProxyHandler<any> = {};
const kGET = Symbol.for("vencord.lazy.get"); export const SYM_LAZY_GET = Symbol.for("vencord.lazy.get");
const kCACHE = Symbol.for("vencord.lazy.cached"); export const SYM_LAZY_CACHED = Symbol.for("vencord.lazy.cached");
for (const method of [ for (const method of [
"apply", "apply",
@ -53,11 +53,11 @@ for (const method of [
"setPrototypeOf" "setPrototypeOf"
]) { ]) {
handler[method] = handler[method] =
(target: any, ...args: any[]) => Reflect[method](target[kGET](), ...args); (target: any, ...args: any[]) => Reflect[method](target[SYM_LAZY_GET](), ...args);
} }
handler.ownKeys = target => { handler.ownKeys = target => {
const v = target[kGET](); const v = target[SYM_LAZY_GET]();
const keys = Reflect.ownKeys(v); const keys = Reflect.ownKeys(v);
for (const key of unconfigurable) { for (const key of unconfigurable) {
if (!keys.includes(key)) keys.push(key); if (!keys.includes(key)) keys.push(key);
@ -69,7 +69,7 @@ handler.getOwnPropertyDescriptor = (target, p) => {
if (typeof p === "string" && unconfigurable.includes(p)) if (typeof p === "string" && unconfigurable.includes(p))
return Reflect.getOwnPropertyDescriptor(target, p); return Reflect.getOwnPropertyDescriptor(target, p);
const descriptor = Reflect.getOwnPropertyDescriptor(target[kGET](), p); const descriptor = Reflect.getOwnPropertyDescriptor(target[SYM_LAZY_GET](), p);
if (descriptor) Object.defineProperty(target, p, descriptor); if (descriptor) Object.defineProperty(target, p, descriptor);
return descriptor; return descriptor;
@ -92,31 +92,34 @@ export function proxyLazy<T>(factory: () => T, attempts = 5, isChild = false): T
let tries = 0; let tries = 0;
const proxyDummy = Object.assign(function () { }, { const proxyDummy = Object.assign(function () { }, {
[kCACHE]: void 0 as T | undefined, [SYM_LAZY_CACHED]: void 0 as T | undefined,
[kGET]() { [SYM_LAZY_GET]() {
if (!proxyDummy[kCACHE] && attempts > tries++) { if (!proxyDummy[SYM_LAZY_CACHED] && attempts > tries++) {
proxyDummy[kCACHE] = factory(); proxyDummy[SYM_LAZY_CACHED] = factory();
if (!proxyDummy[kCACHE] && attempts === tries) if (!proxyDummy[SYM_LAZY_CACHED] && attempts === tries)
console.error("Lazy factory failed:", factory); console.error("Lazy factory failed:", factory);
} }
return proxyDummy[kCACHE]; return proxyDummy[SYM_LAZY_CACHED];
} }
}); });
return new Proxy(proxyDummy, { return new Proxy(proxyDummy, {
...handler, ...handler,
get(target, p, receiver) { get(target, p, receiver) {
if (p === SYM_LAZY_CACHED || p === SYM_LAZY_GET)
return Reflect.get(target, p, receiver);
// if we're still in the same tick, it means the lazy was immediately used. // if we're still in the same tick, it means the lazy was immediately used.
// thus, we lazy proxy the get access to make things like destructuring work as expected // thus, we lazy proxy the get access to make things like destructuring work as expected
// meow here will also be a lazy // meow here will also be a lazy
// `const { meow } = findByPropsLazy("meow");` // `const { meow } = findByPropsLazy("meow");`
if (!isChild && isSameTick) if (!isChild && isSameTick)
return proxyLazy( return proxyLazy(
() => Reflect.get(target[kGET](), p, receiver), () => Reflect.get(target[SYM_LAZY_GET](), p, receiver),
attempts, attempts,
true true
); );
const lazyTarget = target[kGET](); const lazyTarget = target[SYM_LAZY_GET]();
if (typeof lazyTarget === "object" || typeof lazyTarget === "function") { if (typeof lazyTarget === "object" || typeof lazyTarget === "function") {
return Reflect.get(lazyTarget, p, receiver); return Reflect.get(lazyTarget, p, receiver);
} }