commit 7ec0c3c82d9c359917f5ecfe9a9919424be4e215 Author: Beef Date: Tue Oct 18 23:04:55 2022 +0100 [Global] Initial progress diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..04c01ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ \ No newline at end of file diff --git a/build.mjs b/build.mjs new file mode 100644 index 0000000..38f92ca --- /dev/null +++ b/build.mjs @@ -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); +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..5b5b179 --- /dev/null +++ b/package.json @@ -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" + ] + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..3bdb5e5 --- /dev/null +++ b/pnpm-lock.yaml @@ -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 diff --git a/src/def.d.ts b/src/def.d.ts new file mode 100644 index 0000000..45409b1 --- /dev/null +++ b/src/def.d.ts @@ -0,0 +1,45 @@ +import * as _spitroast from "spitroast"; + +type MetroModules = { [id: number]: any }; + +// Helper types for API functions +type PropIntellisense

= Record & Record; +type PropsFinder = (...props: T[]) => PropIntellisense; +type PropsFinderAll = (...props: T[]) => PropIntellisense[]; + +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; + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..ce354f1 --- /dev/null +++ b/src/index.ts @@ -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()}`); +} \ No newline at end of file diff --git a/src/lib/logger.ts b/src/lib/logger.ts new file mode 100644 index 0000000..3bac60e --- /dev/null +++ b/src/lib/logger.ts @@ -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; \ No newline at end of file diff --git a/src/lib/metro/common.ts b/src/lib/metro/common.ts new file mode 100644 index 0000000..9c276fd --- /dev/null +++ b/src/lib/metro/common.ts @@ -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"); \ No newline at end of file diff --git a/src/lib/metro/filters.ts b/src/lib/metro/filters.ts new file mode 100644 index 0000000..3611dd6 --- /dev/null +++ b/src/lib/metro/filters.ts @@ -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)); \ No newline at end of file diff --git a/src/lib/patcher.ts b/src/lib/patcher.ts new file mode 100644 index 0000000..fbff9d6 --- /dev/null +++ b/src/lib/patcher.ts @@ -0,0 +1,4 @@ +import * as _spitroast from "spitroast"; + +export * from "spitroast"; +export default { ..._spitroast }; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2b5f08a --- /dev/null +++ b/tsconfig.json @@ -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/*"] + } + } +}