-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add replacement implementation stores for client mods that don't have specific functions #8
Changes from 6 commits
fe22fa1
55d2225
963b212
05a7ed9
e436edb
30779b6
59bb527
e03ffb4
a4e0f5c
159d03c
553c150
523b6b9
b368814
0e6a5b7
62d6fed
0554c57
64ad446
9c3f101
bc24867
d48383c
e89b902
ed29643
e7215de
7af37ed
705e18f
ec51f87
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { FunctionImplementation, require } from "./index.js"; | ||
import { IModImplementation } from "../api/ModImplementation"; | ||
|
||
// const reservedData = {} as { [key: string]: any }; | ||
|
||
export let targetMod: IModImplementation; | ||
|
||
const implementationStore = { | ||
getModule: new FunctionImplementation({ | ||
depends: [], | ||
supplies: "getModule", | ||
// get data() { | ||
// return reservedData[this.supplies]; | ||
// }, | ||
// set data(v: any) { | ||
// reservedData[this.supplies] = v; | ||
// }, | ||
data: {}, | ||
func(filter: (mod: any) => boolean) { | ||
if (this.data.req == undefined) { | ||
// @ts-expect-error Non-standard property | ||
window.webpackChunkdiscord_app.push( | ||
[[Symbol()], | ||
{}, | ||
(r: { c: any; }) => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes, very readable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's how every discord mod does it I guess |
||
this.data = this.data ?? { | ||
req: r, | ||
}, | ||
]); | ||
// @ts-expect-error Non-standard property | ||
window.webpackChunkdiscord_app.pop(); | ||
} | ||
// @ts-expect-error too lazy | ||
return Object.values(this.data.req).find(filter)?.exports; | ||
}, | ||
}), | ||
getByStrings: new FunctionImplementation({ | ||
depends: ["getModule"], | ||
supplies: "getByStrings", | ||
data: null, | ||
func(...strings) { | ||
const getModule = require(targetMod, "Webpack", "getModule"); | ||
if (!getModule) | ||
throw new Error("Unimplemented"); | ||
return getModule((module: any) => { | ||
if (!module?.toString || typeof (module?.toString) !== "function") return; // Not stringable | ||
let moduleString = ""; | ||
try { moduleString = module?.toString([]); } | ||
catch (err) { moduleString = module?.toString(); } | ||
if (!moduleString) return false; // Could not create string | ||
for (const s of strings) { | ||
if (!moduleString.includes(s)) return false; | ||
} | ||
return true; | ||
}); | ||
}, | ||
}), | ||
} as { [key: string]: FunctionImplementation }; | ||
export { | ||
implementationStore, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { IModImplementation } from "../api/ModImplementation"; | ||
import { getKeyValue } from "../utils.js"; | ||
|
||
export interface IFunctionImplementation { | ||
supplies: string, | ||
depends: string[], | ||
data: any, | ||
func: (...args: any[]) => any, | ||
} | ||
class FunctionImplementation implements IFunctionImplementation { | ||
supplies: string; | ||
depends: string[]; | ||
data: any; | ||
func: (...args: any[]) => any; | ||
// constructor(supplies: string, depends: string[], data: any, func: (...args: any[]) => any) { | ||
// this.supplies = supplies; | ||
// this.depends = depends; | ||
// this.data = data; | ||
// this.func = func; | ||
// } | ||
constructor(options: IFunctionImplementation) { | ||
const { supplies, depends, data, func } = options; | ||
this.supplies = supplies!; | ||
this.depends = depends!; | ||
this.data = data!; | ||
this.func = func!; | ||
} | ||
} | ||
export { | ||
FunctionImplementation, | ||
}; | ||
// import * as WebpackImplementations from "./Webpack.js"; | ||
import { createFunctionFromObjectProperty } from "../api/RuntimeGenerators.js"; | ||
import { readdirSync } from "fs"; | ||
import * as url from 'url'; | ||
import * as path from 'path'; | ||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); | ||
|
||
export const implementationStores = { | ||
// "Webpack": WebpackImplementations, | ||
} as { [category: string]: { implementationStore: { [key: string]: FunctionImplementation }, targetMod: IModImplementation } }; | ||
export async function initStores() { | ||
const stores = readdirSync(`${__dirname}`).filter(x => !x.startsWith("index.")); | ||
console.log(stores); | ||
for (let index = 0; index < stores.length; index++) { | ||
const filler = import(url.pathToFileURL(`${__dirname}/${stores[index]}`).href); | ||
const mod = await filler; | ||
implementationStores[path.parse(stores[index]).name] = mod; | ||
} | ||
console.log("done"); | ||
} | ||
export function doesImplement(mod: IModImplementation, category: string, method: string) { | ||
const categoryObj = getKeyValue(mod, category as keyof IModImplementation); | ||
return getKeyValue(categoryObj, method as never) != undefined; | ||
} | ||
|
||
export function require(mod: IModImplementation, category: string, method: string) { | ||
if (doesImplement(mod, category, method)) { | ||
const categoryObj = getKeyValue(mod, category as keyof IModImplementation); | ||
return getKeyValue(categoryObj, method as never); | ||
} | ||
implementationStores[category].targetMod = mod; | ||
const foundImplementation = implementationStores[category].implementationStore[method]; | ||
if (foundImplementation == undefined) | ||
return null; // depends failed | ||
return createFunctionFromObjectProperty(`globalThis.implementationStores.${category}`, method); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,128 @@ export const myPackageName = (() => { | |
|
||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
export type NonFunctionType<T> = T extends Function ? never : T; | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
export type OnlyFunctionType<T> = T extends Function ? T : never; | ||
export const getKeyValue = <T, K extends keyof T>(obj: T, key: K): T[K] => obj[key]; | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
export const getMain = <T>(t: T | { default: T }) => (typeof t == "function" ? t : (t as { default: T }).default); | ||
Davilarek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
export function escapeJsonString(str: string) { | ||
return str.replace(/["\\]/g, '\\$&') | ||
.replace(/\n/g, '\\n') | ||
.replace(/\r/g, '\\r') | ||
.replace(/\t/g, '\\t') | ||
.replace(/\f/g, '\\f'); | ||
} | ||
/** | ||
* this function tries to create valid JSON with functions embedded | ||
*/ | ||
export function createJavaScriptFromObject(obj_: any, intend = false) { | ||
const tree = [ | ||
"{", | ||
]; | ||
const tree_Tabs = [0]; | ||
function enumerateObject(obj: any, tabOffset = 0, iter = 0) { | ||
if (obj === null) { | ||
return; | ||
} | ||
if (Array.isArray(obj)) { | ||
for (let i = 0; i < obj.length; i++) { | ||
enumerateObject(obj[i], tabOffset, iter + 1); | ||
} | ||
} | ||
else if (typeof obj === 'object') { | ||
for (const key in obj) { | ||
const descriptor = Object.getOwnPropertyDescriptor(obj, key); | ||
if (descriptor && (descriptor.get || descriptor.set)) { | ||
console.log(`Property ${key} is a getter/setter.`); | ||
} | ||
else { | ||
tree.push(key + ": "); | ||
tree_Tabs.push(tabOffset); | ||
if (typeof obj[key] === 'object') { | ||
// console.log(`Object ${key}:`); | ||
const isArray = Array.isArray(obj[key]); | ||
if (isArray) { | ||
tree.push("["); | ||
tree_Tabs.push(tabOffset); | ||
} | ||
else { | ||
tree.push("{"); | ||
tree_Tabs.push(tabOffset); | ||
} | ||
enumerateObject(obj[key], tabOffset + 1, iter + 1); | ||
if (isArray) { | ||
tree.push("],"); | ||
tree_Tabs.push(tabOffset); | ||
} | ||
else { | ||
tree.push("},"); | ||
tree_Tabs.push(tabOffset); | ||
} | ||
} | ||
else { | ||
// console.log(`Key: ${key}, Value: ${obj[key]}`); | ||
// tree.push(`"${obj[key]}",`.replace(/\n/g, "\\n")); | ||
tree.push("\"" + escapeJsonString(`${obj[key]}`) + "\","); | ||
tree_Tabs.push(tabOffset + 1); | ||
} | ||
} | ||
} | ||
} | ||
else { | ||
tree.push(`"${obj}",`.replace(/\n/g, "\\n")); | ||
tree_Tabs.push(tabOffset); | ||
} | ||
} | ||
enumerateObject(obj_, 1); | ||
tree.push("}"); | ||
tree_Tabs.push(0); | ||
// console.log(tree); | ||
if (!intend) { | ||
return tree.join("\n"); | ||
} | ||
const final = []; | ||
for (let index = 0; index < tree.length; index++) { | ||
final.push(new Array(tree_Tabs[index]).fill("\t").join("") + tree[index]); | ||
} | ||
return final.join("\n"); | ||
} | ||
|
||
export type Tree = Record<string, Function | any> | undefined | null; | ||
Davilarek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
export type TreeFilter = string | ((tree: Tree) => boolean | Boolean); // whats the difference :) | ||
Davilarek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
export function findInTree( | ||
tree: Tree, | ||
searchFilter: TreeFilter, | ||
args: { walkable?: string[]; ignore?: string[]; maxRecursion: number } = { maxRecursion: 100 }, | ||
): Tree | null | undefined { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there an explaination on why u returing or null or undefined and not just undefined? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know, this isn't my code |
||
const { walkable, ignore, maxRecursion } = args; | ||
|
||
if (maxRecursion <= 0 || !tree) return undefined; | ||
|
||
// @ts-ignore | ||
// yeah I get all expressions arent callable. that's why IM CHECKING IF IT CAN BE. | ||
if ((typeof searchFilter === "string" && tree?.[searchFilter]) || searchFilter(tree)) | ||
Davilarek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return tree; | ||
|
||
if (!Array.isArray(tree) && typeof tree !== "object") | ||
return undefined; | ||
|
||
const elements = Array.isArray(tree) ? tree : (walkable == null ? Object.keys(tree) : walkable); | ||
|
||
for (const element of elements) { | ||
if (ignore?.includes(element)) continue; | ||
|
||
const subtree = Array.isArray(tree) ? element : tree[element]; | ||
const result = findInTree(subtree, searchFilter, { | ||
walkable, | ||
ignore, | ||
maxRecursion: maxRecursion - 1, | ||
}); | ||
|
||
if (result !== undefined) | ||
return result; | ||
} | ||
|
||
return undefined; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,5 +8,6 @@ | |
"rootDir": "./src", | ||
"strict": true, | ||
// "esModuleInterop": true | ||
"skipLibCheck": true, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too much nesting