From 4250424040ac88c52cc03196d6a074872bde05b5 Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Mon, 19 Aug 2024 23:13:21 -0400 Subject: [PATCH] add type, formatting and refactor --- src/plugins/devCompanion.dev/index.tsx | 256 +++++++++++-------------- src/plugins/devCompanion.dev/util.tsx | 96 ++++++++++ 2 files changed, 206 insertions(+), 146 deletions(-) create mode 100644 src/plugins/devCompanion.dev/util.tsx diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index dc063c26c..edfb0fec5 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -22,7 +22,9 @@ import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; 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 NAV_ID = "dev-companion-reconnect"; @@ -31,82 +33,19 @@ const logger = new Logger("DevCompanion"); let socket: WebSocket | undefined; -type Node = StringNode | RegexNode | FunctionNode; - -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; -} - -const settings = definePluginSettings({ +export const settings = definePluginSettings({ notifyOnAutoConnect: { description: "Whether to notify when Dev Companion has automatically connected.", type: OptionType.BOOLEAN, 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) { let wasConnected = isManual; let hasErrored = false; @@ -191,97 +130,122 @@ function initWs(isManual = false) { logger.info("Received Message:", type, "\n", data); switch (type) { - case "extract": { + case "diff": { const { extractType, idOrSearch } = data; switch (extractType) { case "id": { - console.log("ID!"); - let data; - if (typeof idOrSearch === "number") - data = wreq.m[idOrSearch]?.toString() || null; - else { - return reply(`the provided moduleID is not a number. Got: ${typeof idOrSearch}`); - } - if (!data) - return reply(`Module(${idOrSearch}) not found`); - else - replyData({ - type: "extract", - ok: true, - data, - moduleNumber: idOrSearch - }); - + if (typeof idOrSearch !== "number") + throw new Error("Id is not a number, got :" + typeof idOrSearch); + replyData({ + type: "diff", + ok: true, + data: { + patched: extractOrThrow(idOrSearch), + source: extractModule(idOrSearch, false) + }, + moduleNumber: idOrSearch + }); break; } case "search": { - try { - const moduleId = findModuleId([idOrSearch.toString()]); - const data = wreq.m[moduleId].toString(); + const moduleId = +findModuleId([idOrSearch.toString()]); + replyData({ + 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({ type: "extract", ok: true, - data, - moduleNumber: +moduleId + data: extractModule(moduleId), + moduleNumber: moduleId }); - } catch (e) { - reply("Error: " + String(e)); + break; } - break; - } - case "find": { - const { findType, findArgs } = data; - try { - var parsedArgs = findArgs.map(parseNode); - } 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); + case "find": { + const { findType, findArgs } = data; + try { + var parsedArgs = findArgs.map(parseNode); + } catch (err) { + return reply("Failed to parse args: " + err); } - 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); + 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; + 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: - reply(`Unknown Extract type. Got: ${extractType}`); - break; + } catch (error) { + reply(String(error)); } break; } diff --git a/src/plugins/devCompanion.dev/util.tsx b/src/plugins/devCompanion.dev/util.tsx new file mode 100644 index 000000000..6240e49c4 --- /dev/null +++ b/src/plugins/devCompanion.dev/util.tsx @@ -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; +}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]; +} +