mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-01-26 17:26:22 +00:00
add type, formatting and refactor
This commit is contained in:
parent
6c93b4d7ec
commit
4250424040
2 changed files with 206 additions and 146 deletions
|
@ -22,7 +22,9 @@ import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
||||||
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
||||||
import { CodeFilter, filters, findAll, search, stringMatches, wreq } from "@webpack";
|
import { filters, findAll, search, wreq } from "@webpack";
|
||||||
|
|
||||||
|
import { extractModule, extractOrThrow, FindData, findModuleId, parseNode, PatchData, SendData } from "./util";
|
||||||
|
|
||||||
const PORT = 8485;
|
const PORT = 8485;
|
||||||
const NAV_ID = "dev-companion-reconnect";
|
const NAV_ID = "dev-companion-reconnect";
|
||||||
|
@ -31,82 +33,19 @@ const logger = new Logger("DevCompanion");
|
||||||
|
|
||||||
let socket: WebSocket | undefined;
|
let socket: WebSocket | undefined;
|
||||||
|
|
||||||
type Node = StringNode | RegexNode | FunctionNode;
|
export const settings = definePluginSettings({
|
||||||
|
|
||||||
interface StringNode {
|
|
||||||
type: "string";
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RegexNode {
|
|
||||||
type: "regex";
|
|
||||||
value: {
|
|
||||||
pattern: string;
|
|
||||||
flags: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FunctionNode {
|
|
||||||
type: "function";
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PatchData {
|
|
||||||
find: string;
|
|
||||||
replacement: {
|
|
||||||
match: StringNode | RegexNode;
|
|
||||||
replace: StringNode | FunctionNode;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FindData {
|
|
||||||
type: string;
|
|
||||||
args: Array<StringNode | FunctionNode>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
|
||||||
notifyOnAutoConnect: {
|
notifyOnAutoConnect: {
|
||||||
description: "Whether to notify when Dev Companion has automatically connected.",
|
description: "Whether to notify when Dev Companion has automatically connected.",
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
usePatchedModule: {
|
||||||
|
description: "On extract requests, reply with the current patched module (if it is patched) instead of the original",
|
||||||
|
default: true,
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function parseNode(node: Node) {
|
|
||||||
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
|
|
||||||
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) {
|
|
||||||
throw new Error("More than one match");
|
|
||||||
}
|
|
||||||
return matches[0];
|
|
||||||
}
|
|
||||||
interface SendData {
|
|
||||||
type: string,
|
|
||||||
data: any,
|
|
||||||
ok: boolean;
|
|
||||||
nonce?: number;
|
|
||||||
}
|
|
||||||
function initWs(isManual = false) {
|
function initWs(isManual = false) {
|
||||||
let wasConnected = isManual;
|
let wasConnected = isManual;
|
||||||
let hasErrored = false;
|
let hasErrored = false;
|
||||||
|
@ -191,97 +130,122 @@ function initWs(isManual = false) {
|
||||||
logger.info("Received Message:", type, "\n", data);
|
logger.info("Received Message:", type, "\n", data);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "extract": {
|
case "diff": {
|
||||||
const { extractType, idOrSearch } = data;
|
const { extractType, idOrSearch } = data;
|
||||||
switch (extractType) {
|
switch (extractType) {
|
||||||
case "id": {
|
case "id": {
|
||||||
console.log("ID!");
|
if (typeof idOrSearch !== "number")
|
||||||
let data;
|
throw new Error("Id is not a number, got :" + typeof idOrSearch);
|
||||||
if (typeof idOrSearch === "number")
|
replyData({
|
||||||
data = wreq.m[idOrSearch]?.toString() || null;
|
type: "diff",
|
||||||
else {
|
ok: true,
|
||||||
return reply(`the provided moduleID is not a number. Got: ${typeof idOrSearch}`);
|
data: {
|
||||||
}
|
patched: extractOrThrow(idOrSearch),
|
||||||
if (!data)
|
source: extractModule(idOrSearch, false)
|
||||||
return reply(`Module(${idOrSearch}) not found`);
|
},
|
||||||
else
|
moduleNumber: idOrSearch
|
||||||
replyData({
|
});
|
||||||
type: "extract",
|
|
||||||
ok: true,
|
|
||||||
data,
|
|
||||||
moduleNumber: idOrSearch
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "search": {
|
case "search": {
|
||||||
try {
|
const moduleId = +findModuleId([idOrSearch.toString()]);
|
||||||
const moduleId = findModuleId([idOrSearch.toString()]);
|
replyData({
|
||||||
const data = wreq.m[moduleId].toString();
|
type: "diff",
|
||||||
|
ok: true,
|
||||||
|
data: {
|
||||||
|
patched: extractOrThrow(moduleId),
|
||||||
|
source: extractModule(moduleId, false)
|
||||||
|
},
|
||||||
|
moduleNumber: moduleId
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "extract": {
|
||||||
|
try {
|
||||||
|
const { extractType, idOrSearch } = data;
|
||||||
|
switch (extractType) {
|
||||||
|
case "id": {
|
||||||
|
if (typeof idOrSearch !== "number")
|
||||||
|
throw new Error("Id is not a number, got :" + typeof idOrSearch);
|
||||||
|
else
|
||||||
|
replyData({
|
||||||
|
type: "extract",
|
||||||
|
ok: true,
|
||||||
|
data: extractModule(idOrSearch),
|
||||||
|
moduleNumber: idOrSearch
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "search": {
|
||||||
|
const moduleId = +findModuleId([idOrSearch.toString()]);
|
||||||
replyData({
|
replyData({
|
||||||
type: "extract",
|
type: "extract",
|
||||||
ok: true,
|
ok: true,
|
||||||
data,
|
data: extractModule(moduleId),
|
||||||
moduleNumber: +moduleId
|
moduleNumber: moduleId
|
||||||
});
|
});
|
||||||
} catch (e) {
|
break;
|
||||||
reply("Error: " + String(e));
|
|
||||||
}
|
}
|
||||||
break;
|
case "find": {
|
||||||
}
|
const { findType, findArgs } = data;
|
||||||
case "find": {
|
try {
|
||||||
const { findType, findArgs } = data;
|
var parsedArgs = findArgs.map(parseNode);
|
||||||
try {
|
} catch (err) {
|
||||||
var parsedArgs = findArgs.map(parseNode);
|
return reply("Failed to parse args: " + err);
|
||||||
} catch (err) {
|
|
||||||
return reply("Failed to parse args: " + err);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let results: any[];
|
|
||||||
switch (findType.replace("find", "").replace("Lazy", "")) {
|
|
||||||
case "":
|
|
||||||
results = findAll(parsedArgs[0]);
|
|
||||||
break;
|
|
||||||
case "ByProps":
|
|
||||||
results = findAll(filters.byProps(...parsedArgs));
|
|
||||||
break;
|
|
||||||
case "Store":
|
|
||||||
results = findAll(filters.byStoreName(parsedArgs[0]));
|
|
||||||
break;
|
|
||||||
case "ByCode":
|
|
||||||
results = findAll(filters.byCode(...parsedArgs));
|
|
||||||
break;
|
|
||||||
case "ModuleId":
|
|
||||||
results = Object.keys(search(parsedArgs[0]));
|
|
||||||
break;
|
|
||||||
case "ComponentByCode":
|
|
||||||
results = findAll(filters.componentByCode(...parsedArgs));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return reply("Unknown Find Type " + findType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniqueResultsCount = new Set(results).size;
|
try {
|
||||||
if (uniqueResultsCount === 0) throw "No results";
|
let results: any[];
|
||||||
if (uniqueResultsCount > 1) throw "Found more than one result! Make this filter more specific";
|
switch (findType.replace("find", "").replace("Lazy", "")) {
|
||||||
// best name ever
|
case "":
|
||||||
const foundFind: string = [...results][0].toString();
|
results = findAll(parsedArgs[0]);
|
||||||
replyData({
|
break;
|
||||||
type: "extract",
|
case "ByProps":
|
||||||
ok: true,
|
results = findAll(filters.byProps(...parsedArgs));
|
||||||
find: true,
|
break;
|
||||||
data: foundFind,
|
case "Store":
|
||||||
moduleNumber: +findModuleId([foundFind])
|
results = findAll(filters.byStoreName(parsedArgs[0]));
|
||||||
});
|
break;
|
||||||
} catch (err) {
|
case "ByCode":
|
||||||
return reply("Failed to find: " + err);
|
results = findAll(filters.byCode(...parsedArgs));
|
||||||
|
break;
|
||||||
|
case "ModuleId":
|
||||||
|
results = Object.keys(search(parsedArgs[0]));
|
||||||
|
break;
|
||||||
|
case "ComponentByCode":
|
||||||
|
results = findAll(filters.componentByCode(...parsedArgs));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return reply("Unknown Find Type " + findType);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueResultsCount = new Set(results).size;
|
||||||
|
if (uniqueResultsCount === 0) throw "No results";
|
||||||
|
if (uniqueResultsCount > 1) throw "Found more than one result! Make this filter more specific";
|
||||||
|
// best name ever
|
||||||
|
const foundFind: string = [...results][0].toString();
|
||||||
|
replyData({
|
||||||
|
type: "extract",
|
||||||
|
ok: true,
|
||||||
|
find: true,
|
||||||
|
data: foundFind,
|
||||||
|
moduleNumber: +findModuleId([foundFind])
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
return reply("Failed to find: " + err);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
default:
|
||||||
|
reply(`Unknown Extract type. Got: ${extractType}`);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
} catch (error) {
|
||||||
reply(`Unknown Extract type. Got: ${extractType}`);
|
reply(String(error));
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
96
src/plugins/devCompanion.dev/util.tsx
Normal file
96
src/plugins/devCompanion.dev/util.tsx
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2024 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CodeFilter, stringMatches, wreq } from "@webpack";
|
||||||
|
|
||||||
|
import { settings } from ".";
|
||||||
|
|
||||||
|
type Node = StringNode | RegexNode | FunctionNode;
|
||||||
|
|
||||||
|
export interface StringNode {
|
||||||
|
type: "string";
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
export interface RegexNode {
|
||||||
|
type: "regex";
|
||||||
|
value: {
|
||||||
|
pattern: string;
|
||||||
|
flags: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export interface FunctionNode {
|
||||||
|
type: "function";
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
export interface PatchData {
|
||||||
|
find: string;
|
||||||
|
replacement: {
|
||||||
|
match: StringNode | RegexNode;
|
||||||
|
replace: StringNode | FunctionNode;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
export interface FindData {
|
||||||
|
type: string;
|
||||||
|
args: Array<StringNode | FunctionNode>;
|
||||||
|
}export interface SendData {
|
||||||
|
type: string;
|
||||||
|
data: any;
|
||||||
|
ok: boolean;
|
||||||
|
nonce?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* extracts the patched module, if there is no patched module, throws an error
|
||||||
|
* @param id module id
|
||||||
|
*/
|
||||||
|
export function extractOrThrow(id) {
|
||||||
|
const module = wreq.m[id];
|
||||||
|
if (!module?.$$vencordPatchedSource)
|
||||||
|
throw new Error("No patched module found for module id " + id);
|
||||||
|
return module.$$vencordPatchedSource;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
export function extractModule(id: number, patched = settings.store.usePatchedModule): string {
|
||||||
|
const module = wreq.m[id];
|
||||||
|
if (!module)
|
||||||
|
throw new Error("No module found for module id:" + id);
|
||||||
|
return patched ? module.$$vencordPatchedSource ?? module.original : module.original;
|
||||||
|
} export function parseNode(node: Node) {
|
||||||
|
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) {
|
||||||
|
throw new Error("More than one match");
|
||||||
|
}
|
||||||
|
return matches[0];
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue