diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index ab2be99e0..6e9a287d1 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -16,23 +16,18 @@ * along with this program. If not, see . */ -import { showNotification } from "@api/Notifications"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; -import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; -import { filters, findAll, search, wreq } from "@webpack"; import { reporterData } from "debug/reporterData"; -import { extractModule, extractOrThrow, FindData, findModuleId, FindType, mkRegexFind, parseNode, PatchData, SendData } from "./util"; - -const PORT = 8485; +import { initWs, socket, stopWs } from "./initWs"; +console.log("imported"); +export const PORT = 8485; const NAV_ID = "dev-companion-reconnect"; -const logger = new Logger("DevCompanion"); - -let socket: WebSocket | undefined; +export const logger = new Logger("DevCompanion"); export const settings = definePluginSettings({ notifyOnAutoConnect: { @@ -47,336 +42,12 @@ export const settings = definePluginSettings({ } }); -function initWs(isManual = false) { - let wasConnected = isManual; - let hasErrored = false; - const ws = socket = new WebSocket(`ws://localhost:${PORT}`); - - function replyData(data: T) { - ws.send(JSON.stringify(data)); - } - - ws.addEventListener("open", () => { - wasConnected = true; - - logger.info("Connected to WebSocket"); - - // send module cache to vscode - - replyData({ - type: "moduleList", - data: Object.keys(wreq.m), - ok: true, - }); - // if we are running the reporter with companion integration, send the list to vscode as soon as we can - if (IS_COMPANION_TEST) { - replyData({ - type: "report", - data: reporterData, - ok: true - }); - } - - (settings.store.notifyOnAutoConnect || isManual) && showNotification({ - title: "Dev Companion Connected", - body: "Connected to WebSocket", - noPersist: true - }); - }); - - ws.addEventListener("error", e => { - if (!wasConnected) return; - - hasErrored = true; - - logger.error("Dev Companion Error:", e); - - showNotification({ - title: "Dev Companion Error", - body: (e as ErrorEvent).message || "No Error Message", - color: "var(--status-danger, red)", - noPersist: true - }); - }); - - ws.addEventListener("close", e => { - if (!wasConnected || hasErrored) return; - - logger.info("Dev Companion Disconnected:", e.code, e.reason); - - showNotification({ - title: "Dev Companion Disconnected", - body: e.reason || "No Reason provided", - color: "var(--status-danger, red)", - noPersist: true, - onClick() { - setTimeout(() => { - socket?.close(1000, "Reconnecting"); - initWs(true); - }, 2500); - } - }); - }); - - ws.addEventListener("message", e => { - try { - var { nonce, type, data } = JSON.parse(e.data); - } catch (err) { - logger.error("Invalid JSON:", err, "\n" + e.data); - return; - } - function reply(error?: string) { - const data = { nonce, ok: !error } as Record; - if (error) data.error = error; - - ws.send(JSON.stringify(data)); - } - function replyData(data: T) { - data.nonce = nonce; - ws.send(JSON.stringify(data)); - } - - logger.info("Received Message:", type, "\n", data); - - switch (type) { - case "diff": { - try { - const { extractType, idOrSearch } = data; - switch (extractType) { - case "id": { - 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": { - let moduleId; - if (data.findType === FindType.STRING) - moduleId = +findModuleId([idOrSearch.toString()]); - else - moduleId = +findModuleId(mkRegexFind(idOrSearch)); - const p = extractOrThrow(moduleId); - const p2 = extractModule(moduleId, false); - console.log(p, p2, "done"); - replyData({ - type: "diff", - ok: true, - data: { - patched: p, - source: p2 - }, - moduleNumber: moduleId - }); - break; - } - } - } catch (error) { - reply(String(error)); - } - break; - } - case "reload": { - window.location.reload(); - 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": { - let moduleId; - if (data.findType === FindType.STRING) - moduleId = +findModuleId([idOrSearch.toString()]); - else - moduleId = +findModuleId(mkRegexFind(idOrSearch)); - replyData({ - type: "extract", - 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 "": - 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; - } - default: - reply(`Unknown Extract type. Got: ${extractType}`); - break; - } - } catch (error) { - reply(String(error)); - } - break; - } - case "testPatch": { - const { find, replacement } = data as PatchData; - - let candidates; - if (data.findType === FindType.STRING) - candidates = search(find.toString()); - else - candidates = search(...mkRegexFind(find)); - - // const candidates = search(find); - const keys = Object.keys(candidates); - if (keys.length !== 1) - return reply("Expected exactly one 'find' matches, found " + keys.length); - - const mod = candidates[keys[0]]; - let src = String(mod.original ?? mod).replaceAll("\n", ""); - - if (src.startsWith("function(")) { - src = "0," + src; - } - - let i = 0; - - for (const { match, replace } of replacement) { - i++; - - try { - const matcher = canonicalizeMatch(parseNode(match)); - const replacement = canonicalizeReplace(parseNode(replace), "PlaceHolderPluginName"); - - const newSource = src.replace(matcher, replacement as string); - - if (src === newSource) throw "Had no effect"; - Function(newSource); - - src = newSource; - } catch (err) { - return reply(`Replacement ${i} failed: ${err}`); - } - } - - reply(); - break; - } - case "testFind": { - const { type, args } = data as FindData; - let parsedArgs; - try { - parsedArgs = args.map(parseNode); - } catch (err) { - return reply("Failed to parse args: " + err); - } - - try { - let results: any[]; - switch (type.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 " + type); - } - - 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"; - } catch (err) { - return reply("Failed to find: " + err); - } - - reply(); - break; - } - default: - reply("Unknown Type " + type); - break; - } - }); -} - export default definePlugin({ name: "DevCompanion", description: "Dev Companion Plugin", authors: [Devs.Ven, Devs.sadan], - reporterTestable: ReporterTestable.None, + // we need to be reporter testable to run and give the reuslts + reporterTestable: IS_COMPANION_TEST ? undefined : ReporterTestable.None, settings, toolboxActions: { @@ -387,12 +58,11 @@ export default definePlugin({ }, start() { + console.log(123); + console.log(reporterData); initWs(); }, - stop() { - socket?.close(1000, "Plugin Stopped"); - socket = void 0; - } + stop: stopWs, }); diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx new file mode 100644 index 000000000..ca16ee16b --- /dev/null +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -0,0 +1,350 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { showNotification } from "@api/Notifications"; +import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; +import { filters, findAll, search, wreq } from "@webpack"; +import { reporterData } from "debug/reporterData"; + +import { logger, PORT, settings } from "."; +import { extractModule, extractOrThrow, FindData, findModuleId, FindType, mkRegexFind, parseNode, PatchData, SendData } from "./util"; + +export function stopWs() { + socket?.close(1000, "Plugin Stopped"); + socket = void 0; +} + +export let socket: WebSocket | undefined; + +export function initWs(isManual = false) { + let wasConnected = isManual; + let hasErrored = false; + const ws = socket = new WebSocket(`ws://localhost:${PORT}`); + + function replyData(data: T) { + ws.send(JSON.stringify(data)); + } + + ws.addEventListener("open", () => { + wasConnected = true; + + logger.info("Connected to WebSocket"); + + // send module cache to vscode + replyData({ + type: "moduleList", + data: Object.keys(wreq.m), + ok: true, + }); + // if we are running the reporter with companion integration, send the list to vscode as soon as we can + if (IS_COMPANION_TEST) { + replyData({ + type: "report", + data: reporterData, + ok: true + }); + } + + (settings.store.notifyOnAutoConnect || isManual) && showNotification({ + title: "Dev Companion Connected", + body: "Connected to WebSocket", + noPersist: true + }); + }); + + ws.addEventListener("error", e => { + if (!wasConnected) return; + + hasErrored = true; + + logger.error("Dev Companion Error:", e); + + showNotification({ + title: "Dev Companion Error", + body: (e as ErrorEvent).message || "No Error Message", + color: "var(--status-danger, red)", + noPersist: true + }); + }); + + ws.addEventListener("close", e => { + if (!wasConnected || hasErrored) return; + + logger.info("Dev Companion Disconnected:", e.code, e.reason); + + showNotification({ + title: "Dev Companion Disconnected", + body: e.reason || "No Reason provided", + color: "var(--status-danger, red)", + noPersist: true, + onClick() { + setTimeout(() => { + socket?.close(1000, "Reconnecting"); + initWs(true); + }, 2500); + } + }); + }); + + ws.addEventListener("message", e => { + try { + var { nonce, type, data } = JSON.parse(e.data); + } catch (err) { + logger.error("Invalid JSON:", err, "\n" + e.data); + return; + } + function reply(error?: string) { + const data = { nonce, ok: !error } as Record; + if (error) data.error = error; + + ws.send(JSON.stringify(data)); + } + function replyData(data: T) { + data.nonce = nonce; + ws.send(JSON.stringify(data)); + } + + logger.info("Received Message:", type, "\n", data); + + switch (type) { + case "diff": { + try { + const { extractType, idOrSearch } = data; + switch (extractType) { + case "id": { + 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": { + let moduleId; + if (data.findType === FindType.STRING) + moduleId = +findModuleId([idOrSearch.toString()]); + + else + moduleId = +findModuleId(mkRegexFind(idOrSearch)); + const p = extractOrThrow(moduleId); + const p2 = extractModule(moduleId, false); + console.log(p, p2, "done"); + replyData({ + type: "diff", + ok: true, + data: { + patched: p, + source: p2 + }, + moduleNumber: moduleId + }); + break; + } + } + } catch (error) { + reply(String(error)); + } + break; + } + case "reload": { + reply(); + window.location.reload(); + 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": { + let moduleId; + if (data.findType === FindType.STRING) + moduleId = +findModuleId([idOrSearch.toString()]); + + else + moduleId = +findModuleId(mkRegexFind(idOrSearch)); + replyData({ + type: "extract", + 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 "": + 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; + } + default: + reply(`Unknown Extract type. Got: ${extractType}`); + break; + } + } catch (error) { + reply(String(error)); + } + break; + } + case "testPatch": { + const { find, replacement } = data as PatchData; + + let candidates; + if (data.findType === FindType.STRING) + candidates = search(find.toString()); + + else + candidates = search(...mkRegexFind(find)); + + // const candidates = search(find); + const keys = Object.keys(candidates); + if (keys.length !== 1) + return reply("Expected exactly one 'find' matches, found " + keys.length); + + const mod = candidates[keys[0]]; + let src = String(mod.original ?? mod).replaceAll("\n", ""); + + if (src.startsWith("function(")) { + src = "0," + src; + } + + let i = 0; + + for (const { match, replace } of replacement) { + i++; + + try { + const matcher = canonicalizeMatch(parseNode(match)); + const replacement = canonicalizeReplace(parseNode(replace), "PlaceHolderPluginName"); + + const newSource = src.replace(matcher, replacement as string); + + if (src === newSource) throw "Had no effect"; + Function(newSource); + + src = newSource; + } catch (err) { + return reply(`Replacement ${i} failed: ${err}`); + } + } + + reply(); + break; + } + case "testFind": { + const { type, args } = data as FindData; + let parsedArgs; + try { + parsedArgs = args.map(parseNode); + } catch (err) { + return reply("Failed to parse args: " + err); + } + + try { + let results: any[]; + switch (type.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 " + type); + } + + 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"; + } catch (err) { + return reply("Failed to find: " + err); + } + + reply(); + break; + } + default: + reply("Unknown Type " + type); + break; + } + }); +} +