This commit is contained in:
sadan 2024-08-21 01:41:47 -04:00
parent 4f92052d49
commit 3e79fdab6e
No known key found for this signature in database
2 changed files with 359 additions and 339 deletions

View file

@ -16,23 +16,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { showNotification } from "@api/Notifications";
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
import { filters, findAll, search, wreq } from "@webpack";
import { reporterData } from "debug/reporterData"; import { reporterData } from "debug/reporterData";
import { extractModule, extractOrThrow, FindData, findModuleId, FindType, mkRegexFind, parseNode, PatchData, SendData } from "./util"; import { initWs, socket, stopWs } from "./initWs";
console.log("imported");
const PORT = 8485; export const PORT = 8485;
const NAV_ID = "dev-companion-reconnect"; const NAV_ID = "dev-companion-reconnect";
const logger = new Logger("DevCompanion"); export const logger = new Logger("DevCompanion");
let socket: WebSocket | undefined;
export const settings = definePluginSettings({ export const settings = definePluginSettings({
notifyOnAutoConnect: { 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<T extends SendData>(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<string, unknown>;
if (error) data.error = error;
ws.send(JSON.stringify(data));
}
function replyData<T extends SendData>(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({ export default definePlugin({
name: "DevCompanion", name: "DevCompanion",
description: "Dev Companion Plugin", description: "Dev Companion Plugin",
authors: [Devs.Ven, Devs.sadan], 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, settings,
toolboxActions: { toolboxActions: {
@ -387,12 +58,11 @@ export default definePlugin({
}, },
start() { start() {
console.log(123);
console.log(reporterData);
initWs(); initWs();
}, },
stop() { stop: stopWs,
socket?.close(1000, "Plugin Stopped");
socket = void 0;
}
}); });

View file

@ -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<T extends SendData>(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<string, unknown>;
if (error) data.error = error;
ws.send(JSON.stringify(data));
}
function replyData<T extends SendData>(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;
}
});
}