diff --git a/src/debug/reporterData.ts b/src/debug/reporterData.ts index 1ed7a9cbf..1bce68f67 100644 --- a/src/debug/reporterData.ts +++ b/src/debug/reporterData.ts @@ -17,7 +17,7 @@ interface ErroredPatch extends EvaledPatch { oldModule: string, newModule: string; } -interface ReporterData { +export interface ReporterData { failedPatches: { foundNoModule: Patch[]; hadNoEffect: EvaledPatch[]; diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx index 4df063175..0edb67086 100644 --- a/src/plugins/devCompanion.dev/initWs.tsx +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -14,7 +14,9 @@ import { reporterData } from "debug/reporterData"; import { Settings } from "Vencord"; import { logger, PORT, settings } from "."; -import { extractModule, extractOrThrow, FindData, findModuleId, mkRegexFind, parseNode, PatchData, SendData, toggleEnabled, } from "./util"; +import { Recieve } from "./types"; +import { FullOutgoingMessage, OutgoingMessage } from "./types/send"; +import { extractModule, extractOrThrow, findModuleId, mkRegexFind, parseNode, toggleEnabled, } from "./util"; export function stopWs() { socket?.close(1000, "Plugin Stopped"); @@ -28,7 +30,7 @@ export function initWs(isManual = false) { let hasErrored = false; const ws = socket = new WebSocket(`ws://localhost:${PORT}`); - function replyData(data: T) { + function replyData(data: OutgoingMessage) { ws.send(JSON.stringify(data)); } @@ -40,8 +42,10 @@ export function initWs(isManual = false) { // send module cache to vscode replyData({ type: "moduleList", - data: Object.keys(wreq.m), - ok: true, + data: { + modules: Object.keys(wreq.m) + }, + ok: true }); if (IS_COMPANION_TEST) { @@ -110,66 +114,72 @@ export function initWs(isManual = false) { ws.addEventListener("message", e => { try { - var { nonce, type, data } = JSON.parse(e.data); + var d = JSON.parse(e.data) as Recieve.FullIncomingMessage; } catch (err) { logger.error("Invalid JSON:", err, "\n" + e.data); return; } + /** + * @param error the error to reply with. if there is no error, the reply is a sucess + */ function reply(error?: string) { - const data = { nonce, ok: !error } as Record; + const data = { nonce: d.nonce, ok: !error } as Record; if (error) data.error = error; ws.send(JSON.stringify(data)); } - function replyData(data: T) { - data.nonce = nonce; + function replyData(data: OutgoingMessage) { + const toSend: FullOutgoingMessage = { + ...data, + nonce: d.nonce + }; + // data.nonce = d.nonce; ws.send(JSON.stringify(data)); } - logger.info("Received Message:", type, "\n", data); + logger.info("Received Message:", d.type, "\n", d.data); - switch (type) { + switch (d.type) { case "disable": { - const { enabled, pluginName } = data; - const settings = Settings.plugins[pluginName]; - if (enabled !== settings.enabled) - toggleEnabled(pluginName, reply); + const m = d.data; + const settings = Settings.plugins[m.pluginName]; + if (m.enabled !== settings.enabled) + toggleEnabled(m.pluginName, reply); break; } case "rawId": { - const { id } = data; + const m = d.data; replyData({ + type: "rawId", ok: true, - data: extractModule(id), - type: "ret" + data: extractModule(m.id), }); break; } case "diff": { try { - const { extractType, idOrSearch } = data; - switch (extractType) { + const m = d.data; + switch (m.extractType) { case "id": { - if (typeof idOrSearch !== "number") - throw new Error("Id is not a number, got :" + typeof idOrSearch); + if (typeof m.idOrSearch !== "number") + throw new Error("Id is not a number, got :" + typeof m.idOrSearch); replyData({ type: "diff", ok: true, data: { - patched: extractOrThrow(idOrSearch), - source: extractModule(idOrSearch, false) + patched: extractOrThrow(m.idOrSearch), + source: extractModule(m.idOrSearch, false), + moduleNumber: m.idOrSearch }, - moduleNumber: idOrSearch }); break; } case "search": { - let moduleId; - if (data.findType === "string") - moduleId = +findModuleId([idOrSearch.toString()]); - + let moduleId: number; + if (m.findType === "string") + moduleId = +findModuleId([m.idOrSearch.toString()]); else - moduleId = +findModuleId(mkRegexFind(idOrSearch)); + moduleId = +findModuleId(mkRegexFind(m.idOrSearch)); const p = extractOrThrow(moduleId); const p2 = extractModule(moduleId, false); @@ -178,9 +188,9 @@ export function initWs(isManual = false) { ok: true, data: { patched: p, - source: p2 + source: p2, + moduleNumber: moduleId }, - moduleNumber: moduleId }); break; } @@ -197,48 +207,51 @@ export function initWs(isManual = false) { } case "extract": { try { - const { extractType, idOrSearch } = data; - switch (extractType) { + const m = d.data; + switch (m.extractType) { case "id": { - if (typeof idOrSearch !== "number") - throw new Error("Id is not a number, got :" + typeof idOrSearch); + if (typeof m.idOrSearch !== "number") + throw new Error("Id is not a number, got :" + typeof m.idOrSearch); else replyData({ type: "extract", ok: true, - data: extractModule(idOrSearch), - moduleNumber: idOrSearch + data: { + module: extractModule(m.idOrSearch), + moduleNumber: m.idOrSearch, + }, }); break; } case "search": { let moduleId; - if (data.findType === "string") - moduleId = +findModuleId([idOrSearch.toString()]); + if (m.findType === "string") + moduleId = +findModuleId([m.idOrSearch.toString()]); else - moduleId = +findModuleId(mkRegexFind(idOrSearch)); + moduleId = +findModuleId(mkRegexFind(m.idOrSearch)); replyData({ type: "extract", ok: true, - data: extractModule(moduleId), - moduleNumber: moduleId + data: { + module: extractModule(moduleId), + moduleNumber: moduleId + }, }); break; } case "find": { - const { findType, findArgs } = data; try { - var parsedArgs = findArgs.map(parseNode); + var parsedArgs = m.findArgs.map(parseNode); } catch (err) { return reply("Failed to parse args: " + err); } try { let results: any[]; - switch (findType.replace("find", "").replace("Lazy", "")) { + switch (m.findType.replace("find", "").replace("Lazy", "")) { case "": case "Component": results = findAll(parsedArgs[0]); @@ -259,7 +272,7 @@ export function initWs(isManual = false) { results = findAll(filters.componentByCode(...parsedArgs)); break; default: - return reply("Unknown Find Type " + findType); + return reply("Unknown Find Type " + m.findType); } const uniqueResultsCount = new Set(results).size; @@ -270,9 +283,11 @@ export function initWs(isManual = false) { replyData({ type: "extract", ok: true, - find: true, - data: foundFind, - moduleNumber: +findModuleId([foundFind]) + data: { + module: foundFind, + find: true, + moduleNumber: +findModuleId([foundFind]) + }, }); } catch (err) { return reply("Failed to find: " + err); @@ -280,7 +295,7 @@ export function initWs(isManual = false) { break; } default: - reply(`Unknown Extract type. Got: ${extractType}`); + reply(`Unknown Extract type. Got: ${d.data.extractType}`); break; } } catch (error) { @@ -289,13 +304,13 @@ export function initWs(isManual = false) { break; } case "testPatch": { - const { find, replacement } = data as PatchData; + const m = d.data; let candidates; - if (data.findType === "string") - candidates = search(find.toString()); + if (d.data.findType === "string") + candidates = search(m.find.toString()); else - candidates = search(...mkRegexFind(find)); + candidates = search(...mkRegexFind(m.find)); // const candidates = search(find); const keys = Object.keys(candidates); @@ -311,7 +326,7 @@ export function initWs(isManual = false) { let i = 0; - for (const { match, replace } of replacement) { + for (const { match, replace } of m.replacement) { i++; try { @@ -332,17 +347,16 @@ export function initWs(isManual = false) { break; } case "testFind": { - const { type, args } = data as FindData; - let parsedArgs; + const m = d.data; try { - parsedArgs = args.map(parseNode); + var parsedArgs = m.args.map(parseNode); } catch (err) { return reply("Failed to parse args: " + err); } try { let results: any[]; - switch (type.replace("find", "").replace("Lazy", "")) { + switch (m.type.replace("find", "").replace("Lazy", "")) { case "": case "Component": results = findAll(parsedArgs[0]); @@ -363,7 +377,7 @@ export function initWs(isManual = false) { results = findAll(filters.componentByCode(...parsedArgs)); break; default: - return reply("Unknown Find Type " + type); + return reply("Unknown Find Type " + m.type); } const uniqueResultsCount = new Set(results).size; @@ -414,102 +428,8 @@ export function initWs(isManual = false) { }); break; } - // FIXME: this is just extract but with a different name - case "rawContent": { - 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: "rawContent", - ok: true, - data: extractModule(idOrSearch), - moduleNumber: idOrSearch - }); - - break; - } - case "search": { - let moduleId; - if (data.findType === "string") - moduleId = +findModuleId([idOrSearch.toString()]); - - else - moduleId = +findModuleId(mkRegexFind(idOrSearch)); - replyData({ - type: "rawContent", - ok: true, - data: extractModule(moduleId), - moduleNumber: moduleId - }); - 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 "": - case "Component": - 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: "rawContent", - ok: true, - find: true, - data: foundFind, - moduleNumber: +findModuleId([foundFind]) - }); - } catch (err) { - return reply("Failed to find: " + err); - } - break; - } - default: - reply(`Unknown Extract type. Got: ${extractType}`); - break; - } - } catch (error) { - reply(String(error)); - } - break; - } default: - reply("Unknown Type " + type); + reply("Unknown Type " + (d as any).type); break; } }); diff --git a/src/plugins/devCompanion.dev/types/index.ts b/src/plugins/devCompanion.dev/types/index.ts index 3beb3e42c..18bc68541 100644 --- a/src/plugins/devCompanion.dev/types/index.ts +++ b/src/plugins/devCompanion.dev/types/index.ts @@ -3,3 +3,5 @@ * Copyright (c) 2024 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ + +export * as Recieve from "./recieve"; diff --git a/src/plugins/devCompanion.dev/types/recieve.ts b/src/plugins/devCompanion.dev/types/recieve.ts new file mode 100644 index 000000000..bb82ba48e --- /dev/null +++ b/src/plugins/devCompanion.dev/types/recieve.ts @@ -0,0 +1,140 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +// should be the same types as ./server/types/send.ts in the companion +export type SearchData = + | { + extractType: "id"; + idOrSearch: number; + } + | ( + | { + extractType: "search"; + /** + * stringified regex + */ + idOrSearch: string; + findType: "regex"; + } + | { + extractType: "search"; + idOrSearch: string; + findType: "string"; + } + ); + +export type FindOrSearchData = + | SearchData + | ({ + extractType: "find"; + } & _PrefixKeys<_CapitalizeKeys, "find">); + +export type AnyFindType = + `find${"Component" | "ByProps" | "Store" | "ByCode" | "ModuleId" | "ComponentByCode" | ""}${"Lazy" | ""}`; + +export type StringNode = { + type: "string"; + value: string; +}; + +export type RegexNode = { + type: "regex"; + value: { + pattern: string; + flags: string; + }; +}; + +export type FunctionNode = { + type: "function"; + value: string; +}; + +export type FindNode = StringNode | RegexNode | FunctionNode; + +export type FindData = { + type: AnyFindType; + args: FindNode[]; +}; + +export type IncomingMessage = DisablePlugin | RawId | DiffPatch | Reload | ExtractModule | TestPatch | TestFind | AllModules; +export type FullIncomingMessage = IncomingMessage & { nonce: number; }; + +export type DisablePlugin = { + type: "disable"; + data: { + enabled: boolean; + pluginName: string; + }; +}; + +export type RawId = { + type: "rawId"; + data: { + id: number; + }; +}; + +export type DiffPatch = { + type: "diff"; + data: SearchData; +}; + +export type Reload = { + type: "reload"; + data: null; +}; + +export type ExtractModule = { + type: "extract"; + // FIXME: update client code so you can just pass FindData here + data: FindOrSearchData; +}; + +export type TestPatch = { + type: "testPatch"; + data: ( + | { + findType: "string"; + find: string; + } + | { + findType: "regex"; + /** + * stringified regex + */ + find: string; + } + ) & { + replacement: { + match: StringNode | RegexNode; + replace: StringNode | RegexNode; + }[]; + }; +}; + +export type TestFind = { + type: "testFind"; + data: FindData; +}; + +export type AllModules = { + type: "allModules"; + data: null; +}; + +type _PrefixKeys< + T extends Record, + P extends string, +> = string extends P + ? never + : { + [K in keyof T as K extends string ? `${P}${K}` : never]: T[K]; + }; + +type _CapitalizeKeys> = { + [K in keyof T as K extends string ? Capitalize : never]: T[K]; +}; diff --git a/src/plugins/devCompanion.dev/types/send.ts b/src/plugins/devCompanion.dev/types/send.ts new file mode 100644 index 000000000..713bf9d13 --- /dev/null +++ b/src/plugins/devCompanion.dev/types/send.ts @@ -0,0 +1,62 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +// should be the same types as src/server/types/recieve.ts in the companion +import { ReporterData as IReporterData } from "debug/reporterData"; +export type ReporterData = IReporterData; + +export type OutgoingMessage = (Report | DiffModule | ExtractModule | ModuleList | RawId) & Base; +export type FullOutgoingMessage = OutgoingMessage & Nonce; + +export type Base = { + ok: true; +} | { + ok: false; + error: string; +}; +export type Nonce = { + nonce: number; +}; +export type ModuleResult = { + moduleNumber: number; +}; + +// #region valid payloads +export type Report = { + type: "report"; + data: ReporterData; +}; + +export type DiffModule = { + type: "diff"; + data: { + source: string; + patched: string; + } & ModuleResult; +}; + +export type ExtractModule = { + type: "extract"; + data: { + module: string; + /** + * if the module is incomplete. ie: from a find + */ + find?: boolean; + } & ModuleResult; +}; + +export type ModuleList = { + type: "moduleList"; + data: { + modules: string[]; + }; +}; +export type RawId = { + type: "rawId"; + data: string; +}; +// #endregion diff --git a/src/plugins/devCompanion.dev/util.tsx b/src/plugins/devCompanion.dev/util.tsx index 9765ae908..8e1729767 100644 --- a/src/plugins/devCompanion.dev/util.tsx +++ b/src/plugins/devCompanion.dev/util.tsx @@ -11,46 +11,13 @@ import { CodeFilter, stringMatches, wreq } from "@webpack"; import { Toasts } from "@webpack/common"; import { settings as companionSettings } from "."; +import { FindNode } from "./types/recieve"; -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) { +export function extractOrThrow(id: number): string { const module = wreq.m[id]; if (!module?.$$vencordPatchedSource) throw new Error("No patched module found for module id " + id); @@ -70,7 +37,7 @@ export function extractModule(id: number, patched = companionSettings.store.useP throw new Error("No module found for module id:" + id); return patched ? module.$$vencordPatchedSource ?? module.original.toString() : module.original.toString(); } -export function parseNode(node: Node) { +export function parseNode(node: FindNode): any { switch (node.type) { case "string": return node.value; @@ -116,7 +83,7 @@ function showErrorToast(message: string) { }); } -export function toggleEnabled(name: string, beforeReload: () => void) { +export function toggleEnabled(name: string, beforeReload: (error?: string) => void) { let restartNeeded = false; function onRestartNeeded() { restartNeeded = true;