[Global] Initial progress

This commit is contained in:
Beef 2022-10-18 23:04:55 +01:00
commit 7ec0c3c82d
11 changed files with 581 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules/
dist/

27
build.mjs Normal file
View file

@ -0,0 +1,27 @@
import { build } from "esbuild";
import alias from "esbuild-plugin-alias";
import fs from "fs/promises";
import path from "path";
const tsconfig = JSON.parse(await fs.readFile("./tsconfig.json"));
const aliases = Object.fromEntries(Object.entries(tsconfig.compilerOptions.paths).map(([alias, [target]]) => [alias, path.resolve(target)]));
try {
await build({
entryPoints: ["./src/index.ts"],
outfile: "./dist/vendetta.js",
minify: true,
bundle: true,
format: "iife",
external: ["react", "react-native"],
target: "esnext",
plugins: [alias(aliases)],
legalComments: "external",
});
await fs.appendFile("./dist/vendetta.js", "//# sourceURL=Vendetta");
console.log("Build successful!");
} catch (e) {
console.error("Build failed...", e);
process.exit(1);
}

36
package.json Normal file
View file

@ -0,0 +1,36 @@
{
"name": "vendetta",
"version": "1.0.0",
"description": "A mod for Discord's mobile apps.",
"scripts": {
"build": "node build.mjs"
},
"keywords": [
"discord",
"android",
"ios",
"react native"
],
"author": "Beef",
"license": "BSD-3-Clause",
"devDependencies": {
"@types/react": "^18.0.21",
"@types/react-native": "^0.70.5",
"esbuild": "^0.15.11",
"esbuild-plugin-alias": "^0.2.1",
"redux": "^4.2.0",
"typescript": "^4.8.4",
"zustand": "^4.1.2"
},
"dependencies": {
"spitroast": "^1.4.2"
},
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"react",
"react-native"
]
}
}
}

334
pnpm-lock.yaml Normal file
View file

@ -0,0 +1,334 @@
lockfileVersion: 5.4
specifiers:
'@types/react': ^18.0.21
'@types/react-native': ^0.70.5
esbuild: ^0.15.11
esbuild-plugin-alias: ^0.2.1
redux: ^4.2.0
spitroast: ^1.4.2
typescript: ^4.8.4
zustand: ^4.1.2
dependencies:
spitroast: 1.4.2
devDependencies:
'@types/react': 18.0.21
'@types/react-native': 0.70.5
esbuild: 0.15.11
esbuild-plugin-alias: 0.2.1
redux: 4.2.0
typescript: 4.8.4
zustand: 4.1.2
packages:
/@babel/runtime/7.19.4:
resolution: {integrity: sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.10
dev: true
/@esbuild/android-arm/0.15.11:
resolution: {integrity: sha512-PzMcQLazLBkwDEkrNPi9AbjFt6+3I7HKbiYF2XtWQ7wItrHvEOeO3T8Am434zAozWtVP7lrTue1bEfc2nYWeCA==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64/0.15.11:
resolution: {integrity: sha512-geWp637tUhNmhL3Xgy4Bj703yXB9dqiLJe05lCUfjSFDrQf9C/8pArusyPUbUbPwlC/EAUjBw32sxuIl/11dZw==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@types/prop-types/15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
dev: true
/@types/react-native/0.70.5:
resolution: {integrity: sha512-Z+NdP5EB1qGSHPSOcsrXqxXrxlFyp/GBllajUIEQ0+XVSFd1l/B+nmHBvACiZvDwRVE/l2hL2pBWeOUPi2C1JA==}
dependencies:
'@types/react': 18.0.21
dev: true
/@types/react/18.0.21:
resolution: {integrity: sha512-7QUCOxvFgnD5Jk8ZKlUAhVcRj7GuJRjnjjiY/IUBWKgOlnvDvTMLD4RTF7NPyVmbRhNrbomZiOepg7M/2Kj1mA==}
dependencies:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.2
csstype: 3.1.1
dev: true
/@types/scheduler/0.16.2:
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
dev: true
/csstype/3.1.1:
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
dev: true
/esbuild-android-64/0.15.11:
resolution: {integrity: sha512-rrwoXEiuI1kaw4k475NJpexs8GfJqQUKcD08VR8sKHmuW9RUuTR2VxcupVvHdiGh9ihxL9m3lpqB1kju92Ialw==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-android-arm64/0.15.11:
resolution: {integrity: sha512-/hDubOg7BHOhUUsT8KUIU7GfZm5bihqssvqK5PfO4apag7YuObZRZSzViyEKcFn2tPeHx7RKbSBXvAopSHDZJQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-64/0.15.11:
resolution: {integrity: sha512-1DqHD0ms3AhiwkKnjRUzmiW7JnaJJr5FKrPiR7xuyMwnjDqvNWDdMq4rKSD9OC0piFNK6n0LghsglNMe2MwJtA==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-arm64/0.15.11:
resolution: {integrity: sha512-OMzhxSbS0lwwrW40HHjRCeVIJTURdXFA8c3GU30MlHKuPCcvWNUIKVucVBtNpJySXmbkQMDJdJNrXzNDyvoqvQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-64/0.15.11:
resolution: {integrity: sha512-8dKP26r0/Qyez8nTCwpq60QbuYKOeBygdgOAWGCRalunyeqWRoSZj9TQjPDnTTI9joxd3QYw3UhVZTKxO9QdRg==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-arm64/0.15.11:
resolution: {integrity: sha512-aSGiODiukLGGnSg/O9+cGO2QxEacrdCtCawehkWYTt5VX1ni2b9KoxpHCT9h9Y6wGqNHmXFnB47RRJ8BIqZgmQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-32/0.15.11:
resolution: {integrity: sha512-lsrAfdyJBGx+6aHIQmgqUonEzKYeBnyfJPkT6N2dOf1RoXYYV1BkWB6G02tjsrz1d5wZzaTc3cF+TKmuTo/ZwA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-64/0.15.11:
resolution: {integrity: sha512-Y2Rh+PcyVhQqXKBTacPCltINN3uIw2xC+dsvLANJ1SpK5NJUtxv8+rqWpjmBgaNWKQT1/uGpMmA9olALy9PLVA==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm/0.15.11:
resolution: {integrity: sha512-TJllTVk5aSyqPFvvcHTvf6Wu1ZKhWpJ/qNmZO8LL/XeB+LXCclm7HQHNEIz6MT7IX8PmlC1BZYrOiw2sXSB95A==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm64/0.15.11:
resolution: {integrity: sha512-uhcXiTwTmD4OpxJu3xC5TzAAw6Wzf9O1XGWL448EE9bqGjgV1j+oK3lIHAfsHnuIn8K4nDW8yjX0Sv5S++oRuw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-mips64le/0.15.11:
resolution: {integrity: sha512-WD61y/R1M4BLe4gxXRypoQ0Ci+Vjf714QYzcPNkiYv5I8K8WDz2ZR8Bm6cqKxd6rD+e/rZgPDbhQ9PCf7TMHmA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-ppc64le/0.15.11:
resolution: {integrity: sha512-JVleZS9oPVLTlBhPTWgOwxFWU/wMUdlBwTbGA4GF8c38sLbS13cupj+C8bLq929jU7EMWry4SaL+tKGIaTlqKg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-riscv64/0.15.11:
resolution: {integrity: sha512-9aLIalZ2HFHIOZpmVU11sEAS9F8TnHw49daEjcgMpBXHFF57VuT9f9/9LKJhw781Gda0P9jDkuCWJ0tFbErvJw==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-s390x/0.15.11:
resolution: {integrity: sha512-sZHtiXXOKsLI3XGBGoYO4qKBzJlb8xNsWmvFiwFMHFzA4AXgDP1KDp7Dawe9C2pavTRBDvl+Ok4n/DHQ59oaTg==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-netbsd-64/0.15.11:
resolution: {integrity: sha512-hUC9yN06K9sg7ju4Vgu9ChAPdsEgtcrcLfyNT5IKwKyfpLvKUwCMZSdF+gRD3WpyZelgTQfJ+pDx5XFbXTlB0A==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-openbsd-64/0.15.11:
resolution: {integrity: sha512-0bBo9SQR4t66Wd91LGMAqmWorzO0TTzVjYiifwoFtel8luFeXuPThQnEm5ztN4g0fnvcp7AnUPPzS/Depf17wQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-plugin-alias/0.2.1:
resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==}
dev: true
/esbuild-sunos-64/0.15.11:
resolution: {integrity: sha512-EuBdTGlsMTjEl1sQnBX2jfygy7iR6CKfvOzi+gEOfhDqbHXsmY1dcpbVtcwHAg9/2yUZSfMJHMAgf1z8M4yyyw==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-32/0.15.11:
resolution: {integrity: sha512-O0/Wo1Wk6dc0rZSxkvGpmTNIycEznHmkObTFz2VHBhjPsO4ZpCgfGxNkCpz4AdAIeMczpTXt/8d5vdJNKEGC+Q==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-64/0.15.11:
resolution: {integrity: sha512-x977Q4HhNjnHx00b4XLAnTtj5vfbdEvkxaQwC1Zh5AN8g5EX+izgZ6e5QgqJgpzyRNJqh4hkgIJF1pyy1be0mQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-arm64/0.15.11:
resolution: {integrity: sha512-VwUHFACuBahrvntdcMKZteUZ9HaYrBRODoKe4tIWxguQRvvYoYb7iu5LrcRS/FQx8KPZNaa72zuqwVtHeXsITw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild/0.15.11:
resolution: {integrity: sha512-OgHGuhlfZ//mToxjte1D5iiiQgWfJ2GByVMwEC/IuoXsBGkuyK1+KrjYu0laSpnN/L1UmLUCv0s25vObdc1bVg==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.15.11
'@esbuild/linux-loong64': 0.15.11
esbuild-android-64: 0.15.11
esbuild-android-arm64: 0.15.11
esbuild-darwin-64: 0.15.11
esbuild-darwin-arm64: 0.15.11
esbuild-freebsd-64: 0.15.11
esbuild-freebsd-arm64: 0.15.11
esbuild-linux-32: 0.15.11
esbuild-linux-64: 0.15.11
esbuild-linux-arm: 0.15.11
esbuild-linux-arm64: 0.15.11
esbuild-linux-mips64le: 0.15.11
esbuild-linux-ppc64le: 0.15.11
esbuild-linux-riscv64: 0.15.11
esbuild-linux-s390x: 0.15.11
esbuild-netbsd-64: 0.15.11
esbuild-openbsd-64: 0.15.11
esbuild-sunos-64: 0.15.11
esbuild-windows-32: 0.15.11
esbuild-windows-64: 0.15.11
esbuild-windows-arm64: 0.15.11
dev: true
/redux/4.2.0:
resolution: {integrity: sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==}
dependencies:
'@babel/runtime': 7.19.4
dev: true
/regenerator-runtime/0.13.10:
resolution: {integrity: sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==}
dev: true
/spitroast/1.4.2:
resolution: {integrity: sha512-iEBsKg3EXTQj2nikYIMtOE5YSqbI5CtRxVYI+Gh+9HeQxf4u86UWF5yC5eTVAoReZSogbD2M37JYG8TYGBnFTg==}
dev: false
/typescript/4.8.4:
resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/use-sync-external-store/1.2.0:
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
peerDependenciesMeta:
react:
optional: true
dev: true
/zustand/4.1.2:
resolution: {integrity: sha512-gcRaKchcxFPbImrBb/BKgujOhHhik9YhVpIeP87ETT7uokEe2Szu7KkuZ9ghjtD+/KKkcrRNktR2AiLXPIbKIQ==}
engines: {node: '>=12.7.0'}
peerDependencies:
immer: '>=9.0'
react: '>=16.8'
peerDependenciesMeta:
immer:
optional: true
react:
optional: true
dependencies:
use-sync-external-store: 1.2.0
dev: true

45
src/def.d.ts vendored Normal file
View file

@ -0,0 +1,45 @@
import * as _spitroast from "spitroast";
type MetroModules = { [id: number]: any };
// Helper types for API functions
type PropIntellisense<P extends string | symbol> = Record<P, any> & Record<PropertyKey, any>;
type PropsFinder = <T extends string | symbol>(...props: T[]) => PropIntellisense<T>;
type PropsFinderAll = <T extends string | symbol>(...props: T[]) => PropIntellisense<T>[];
type LoggerFunction = (...messages: any[]) => void;
interface Logger {
log: LoggerFunction,
info: LoggerFunction,
warn: LoggerFunction,
error: LoggerFunction,
time: LoggerFunction,
trace: LoggerFunction,
verbose: LoggerFunction,
}
interface VendettaObject {
patcher: {
after: typeof _spitroast.after;
before: typeof _spitroast.before;
instead: typeof _spitroast.instead;
unpatchAll: typeof _spitroast.unpatchAll;
}
metro: {
findByProps: PropsFinder;
findByPropsAll: PropsFinderAll;
findByDisplayName: (name: string, defaultExp: boolean) => any;
findByDisplayNameAll: (name: string, defaultExp: boolean) => any[];
// TODO: Proper typing for common modules
common: Object;
}
logger: Logger;
}
declare global {
interface Window {
[key: PropertyKey]: any;
modules: MetroModules;
vendetta: VendettaObject;
}
}

18
src/index.ts Normal file
View file

@ -0,0 +1,18 @@
import patcher from "@lib/patcher";
import logger from "@lib/logger";
import * as metro from "@metro/filters";
import * as common from "@metro/common";
console.log("Hello from Vendetta!");
try {
window.vendetta = {
patcher: patcher,
metro: { ...metro, common: common },
logger: logger,
};
logger.log("Vendetta is ready!");
} catch (e: Error | any) {
alert(`Vendetta failed to initialize...\n${e.stack || e.toString()}`);
}

7
src/lib/logger.ts Normal file
View file

@ -0,0 +1,7 @@
import { Logger } from "@types";
import { findByProps } from "@metro/filters";
const logModule = findByProps("setLogFn").default;
const logger: Logger = new logModule("Vendetta");
export default logger;

6
src/lib/metro/common.ts Normal file
View file

@ -0,0 +1,6 @@
import { findByProps } from "@metro/filters";
// Discord
export const Constants = findByProps("API_HOST");
export const channels = findByProps("getVoiceChannelId");
export const i18n = findByProps("Messages");

80
src/lib/metro/filters.ts Normal file
View file

@ -0,0 +1,80 @@
import { MetroModules, PropsFinder, PropsFinderAll } from "@types";
// Metro require
declare const __r: (moduleId: number) => any;
let moment: any;
// Function to blacklist a module, preventing it from being searched again
function blacklist(id: number) {
Object.defineProperty(window.modules, id, {
value: window.modules[id],
enumerable: false,
configurable: true,
writable: true
})
}
// Blacklist any "bad-actor" modules
for (const key in window.modules) {
const id = Number(key);
const module = window.modules[id].publicModule.exports;
if (!moment && module && module.isMoment) moment = module;
if (!module || module === window || module["proxygone"] === null) {
blacklist(id);
continue;
}
}
// Get the previous moment locale
const previousLocale = moment?.locale();
// Function to filter through modules
export const filterModules = (modules: MetroModules, single = false) => (filter: (m: any) => boolean) => {
const found = [];
for (const key in modules) {
const id = Number(key);
const module = modules[id].publicModule.exports;
if (!modules[id].isInitialized) try {
__r(id);
// Set moment locale, sort of crappy fix but works I guess
if (previousLocale) moment.locale(previousLocale);
} catch {};
if (!module) {
blacklist(id);
continue;
};
try {
if (module.default && module.__esModule && filter(module.default)) {
if (single) return module.default;
found.push(module.default);
}
if(filter(module)) {
if (single) return module;
else found.push(module);
}
} catch (e: Error | any) {
console.error(`Failed to getModule... ${e.stack || e.toString()}`);
}
}
if (!single) return found;
}
export const modules = window.modules;
export const find = filterModules(modules, true);
export const findAll = filterModules(modules);
const propsFilter = (props: (string | symbol)[]) => (m: any) => props.every((p) => m[p] !== undefined);
const dNameFilter = (name: string, defaultExp: boolean) => (defaultExp ? (m: any) => m.name === name : (m: any) => m?.default?.name === name);
export const findByProps: PropsFinder = (...props) => find(propsFilter(props));
export const findByPropsAll: PropsFinderAll = (...props) => findAll(propsFilter(props));
export const findByDisplayName = (name: string, defaultExp = true) => find(dNameFilter(name, defaultExp));
export const findByDisplayNameAll = (name: string, defaultExp = true) => findAll(dNameFilter(name, defaultExp));

4
src/lib/patcher.ts Normal file
View file

@ -0,0 +1,4 @@
import * as _spitroast from "spitroast";
export * from "spitroast";
export default { ..._spitroast };

22
tsconfig.json Normal file
View file

@ -0,0 +1,22 @@
{
"include": ["src"],
"exclude": ["node_modules"],
"compilerOptions": {
"baseUrl": ".",
"sourceMap": true,
"module": "ESNext",
"target": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"jsx": "react",
"strict": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"paths": {
"@/*": ["src/*"],
"@types": ["src/def.d.ts"],
"@lib/*": ["src/lib/*"],
"@metro/*": ["src/lib/metro/*"]
}
}
}