2024-08-19 23:13:21 -04:00
|
|
|
/*
|
|
|
|
|
* Vencord, a Discord client mod
|
|
|
|
|
* Copyright (c) 2024 Vendicated and contributors
|
|
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
*/
|
|
|
|
|
|
2024-08-28 01:35:47 -04:00
|
|
|
import { showNotice } from "@api/Notices";
|
|
|
|
|
import { Settings } from "@api/Settings";
|
2024-08-20 16:03:02 -04:00
|
|
|
import { canonicalizeMatch } from "@utils/patches";
|
2024-08-19 23:13:21 -04:00
|
|
|
import { CodeFilter, stringMatches, wreq } from "@webpack";
|
2024-08-28 01:35:47 -04:00
|
|
|
import { Toasts } from "@webpack/common";
|
2024-08-19 23:13:21 -04:00
|
|
|
|
2025-02-11 13:56:19 -05:00
|
|
|
import { logger, settings as companionSettings } from ".";
|
|
|
|
|
import { FindNode } from "./types/recieve";
|
2024-08-19 23:13:21 -04:00
|
|
|
|
2025-02-11 13:56:19 -05:00
|
|
|
const { WebpackPatcher: { getFactoryPatchedBy, getFactoryPatchedSource } } = require("Vencord") as typeof import("Vencord");
|
2025-02-11 13:53:43 -05:00
|
|
|
|
2024-08-19 23:13:21 -04:00
|
|
|
/**
|
|
|
|
|
* extracts the patched module, if there is no patched module, throws an error
|
|
|
|
|
* @param id module id
|
|
|
|
|
*/
|
2025-02-11 13:56:19 -05:00
|
|
|
export function extractOrThrow(id: PropertyKey): string {
|
2025-02-11 13:53:43 -05:00
|
|
|
const patchedSource = getFactoryPatchedSource(id);
|
|
|
|
|
if (!patchedSource)
|
2025-02-11 13:56:19 -05:00
|
|
|
throw new Error(`No patched module found for module id ${String(id)}`);
|
2025-02-11 13:53:43 -05:00
|
|
|
return patchedSource;
|
2024-08-19 23:13:21 -04:00
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* attempts to extract the module, throws if not found
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* if patched is true and no patched module is found fallsback to the non-patched module
|
|
|
|
|
* @param id module id
|
|
|
|
|
* @param patched return the patched module
|
|
|
|
|
*/
|
2025-02-11 13:56:19 -05:00
|
|
|
export function extractModule(id: PropertyKey, patched = companionSettings.store.usePatchedModule): string {
|
|
|
|
|
if (patched) {
|
|
|
|
|
try {
|
|
|
|
|
return extractOrThrow(id);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
logger.debug(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return extractUnpatchedModule(id);
|
|
|
|
|
}
|
|
|
|
|
function extractUnpatchedModule(id: PropertyKey): string {
|
|
|
|
|
if (!wreq.m[id]) {
|
|
|
|
|
throw new Error(`Module not found for id: ${String(id)}`);
|
|
|
|
|
}
|
|
|
|
|
return `// Webpack Module ${String(id)} - Patched by\n0,${wreq.m[id]}\n//# sourceURL=WebpackModule${String(id)}`;
|
2024-08-27 17:02:04 -04:00
|
|
|
}
|
2025-02-07 20:49:57 -05:00
|
|
|
|
2025-02-11 13:56:19 -05:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param usePatched if false, always returns `[]`, otherwise uses the same setting as {@link extractModule}
|
|
|
|
|
*/
|
|
|
|
|
export function getModulePatchedBy(id: PropertyKey, usePatched = companionSettings.store.usePatchedModule): string[] {
|
|
|
|
|
return [...usePatched && getFactoryPatchedBy(id) || []];
|
|
|
|
|
}
|
|
|
|
|
export function parseNode(node: FindNode): any {
|
2024-08-19 23:13:21 -04:00
|
|
|
switch (node.type) {
|
|
|
|
|
case "string":
|
|
|
|
|
return node.value;
|
|
|
|
|
case "regex":
|
|
|
|
|
return new RegExp(node.value.pattern, node.value.flags);
|
|
|
|
|
case "function":
|
|
|
|
|
// We LOVE remote code execution
|
|
|
|
|
// Safety: This comes from localhost only, which actually means we have less permissions than the source,
|
|
|
|
|
// since we're running in the browser sandbox, whereas the sender has host access
|
|
|
|
|
return (0, eval)(node.value);
|
|
|
|
|
default:
|
|
|
|
|
throw new Error("Unknown Node Type " + (node as any).type);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// we need to have our own because the one in webpack returns the first with no handling of more than one module
|
|
|
|
|
export function findModuleId(find: CodeFilter) {
|
|
|
|
|
const matches: string[] = [];
|
|
|
|
|
for (const id in wreq.m) {
|
|
|
|
|
if (stringMatches(wreq.m[id].toString(), find)) matches.push(id);
|
|
|
|
|
}
|
|
|
|
|
if (matches.length === 0) {
|
|
|
|
|
throw new Error("No Matches Found");
|
|
|
|
|
}
|
|
|
|
|
if (matches.length !== 1) {
|
2024-08-28 07:24:56 +12:00
|
|
|
throw new Error(`This filter matches ${matches.length} modules. Make it more specific!`);
|
2024-08-19 23:13:21 -04:00
|
|
|
}
|
|
|
|
|
return matches[0];
|
|
|
|
|
}
|
2024-08-20 16:03:02 -04:00
|
|
|
export function mkRegexFind(idOrSearch: string): RegExp[] {
|
|
|
|
|
const regex = idOrSearch.substring(1, idOrSearch.lastIndexOf("/"));
|
|
|
|
|
const flags = idOrSearch.substring(idOrSearch.lastIndexOf("/") + 1);
|
|
|
|
|
return [canonicalizeMatch(RegExp(regex, flags))];
|
|
|
|
|
}
|
2024-08-28 01:35:47 -04:00
|
|
|
// the next two functions are copied from components/pluginSettings
|
|
|
|
|
function showErrorToast(message: string) {
|
|
|
|
|
Toasts.show({
|
|
|
|
|
message,
|
|
|
|
|
type: Toasts.Type.FAILURE,
|
|
|
|
|
id: Toasts.genId(),
|
|
|
|
|
options: {
|
|
|
|
|
position: Toasts.Position.BOTTOM
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-09 02:58:32 -05:00
|
|
|
export function toggleEnabled(name: string, beforeReload: (error?: string) => void) {
|
2024-08-28 01:35:47 -04:00
|
|
|
let restartNeeded = false;
|
|
|
|
|
function onRestartNeeded() {
|
|
|
|
|
restartNeeded = true;
|
|
|
|
|
}
|
|
|
|
|
function beforeReturn() {
|
|
|
|
|
if (restartNeeded) {
|
|
|
|
|
if (companionSettings.store.reloadAfterToggle) {
|
|
|
|
|
beforeReload();
|
|
|
|
|
window.location.reload();
|
|
|
|
|
}
|
|
|
|
|
Toasts.show({
|
|
|
|
|
id: Toasts.genId(),
|
|
|
|
|
message: "Reload Needed",
|
|
|
|
|
type: Toasts.Type.MESSAGE,
|
|
|
|
|
options: {
|
|
|
|
|
duration: 5000,
|
|
|
|
|
position: Toasts.Position.TOP
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const plugin = Vencord.Plugins.plugins[name];
|
|
|
|
|
|
|
|
|
|
const settings = Settings.plugins[plugin.name];
|
|
|
|
|
|
|
|
|
|
const isEnabled = () => settings.enabled ?? false;
|
|
|
|
|
|
|
|
|
|
const wasEnabled = isEnabled();
|
|
|
|
|
|
|
|
|
|
// If we're enabling a plugin, make sure all deps are enabled recursively.
|
|
|
|
|
if (!wasEnabled) {
|
|
|
|
|
const { restartNeeded, failures } = Vencord.Plugins.startDependenciesRecursive(plugin);
|
|
|
|
|
if (failures.length) {
|
|
|
|
|
console.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`);
|
|
|
|
|
showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null);
|
|
|
|
|
beforeReturn();
|
|
|
|
|
return;
|
|
|
|
|
} else if (restartNeeded) {
|
|
|
|
|
// If any dependencies have patches, don't start the plugin yet.
|
|
|
|
|
settings.enabled = true;
|
|
|
|
|
onRestartNeeded();
|
|
|
|
|
beforeReturn();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-19 23:13:21 -04:00
|
|
|
|
2024-08-28 01:35:47 -04:00
|
|
|
// if the plugin has patches, dont use stopPlugin/startPlugin. Wait for restart to apply changes.
|
|
|
|
|
if (plugin.patches?.length) {
|
|
|
|
|
settings.enabled = !wasEnabled;
|
|
|
|
|
onRestartNeeded();
|
|
|
|
|
beforeReturn();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the plugin is enabled, but hasn't been started, then we can just toggle it off.
|
|
|
|
|
if (wasEnabled && !plugin.started) {
|
|
|
|
|
settings.enabled = !wasEnabled;
|
|
|
|
|
beforeReturn();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = wasEnabled ? Vencord.Plugins.stopPlugin(plugin) : Vencord.Plugins.startPlugin(plugin);
|
|
|
|
|
|
|
|
|
|
if (!result) {
|
|
|
|
|
settings.enabled = false;
|
|
|
|
|
|
|
|
|
|
const msg = `Error while ${wasEnabled ? "stopping" : "starting"} plugin ${plugin.name}`;
|
|
|
|
|
console.error(msg);
|
|
|
|
|
showErrorToast(msg);
|
|
|
|
|
beforeReturn();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
settings.enabled = !wasEnabled;
|
|
|
|
|
beforeReturn();
|
|
|
|
|
}
|