parent
2c2508bcae
commit
67b31cbf25
6 changed files with 119 additions and 223 deletions
|
|
@ -1,230 +1,36 @@
|
|||
import network from "./lib/network";
|
||||
import console from "./lib/console";
|
||||
import miscellanous from "./lib/miscellanous";
|
||||
import sentry from "./lib/sentry";
|
||||
import { defineCorePlugin } from "..";
|
||||
import { findByProps } from "@metro";
|
||||
import { instead } from "@lib/api/patcher";
|
||||
import { logger } from "@lib/utils/logger";
|
||||
|
||||
// Type for functions that need to be cleaned up when plugin is disabled
|
||||
type PatchCleanupFn = () => void;
|
||||
|
||||
// Store all active patches for cleanup
|
||||
let patches: PatchCleanupFn[] = [];
|
||||
|
||||
// AnalyticsUtils and other tracking utilities
|
||||
const AnalyticsUtils = findByProps("AnalyticsActionHandlers");
|
||||
const SuperPropUtils = findByProps("encodeProperties", "track");
|
||||
const VoiceStateUtils = findByProps("getVoiceStateMetadata");
|
||||
const CrashReportUtils = findByProps("submitLiveCrashReport");
|
||||
const MetricsUtils = findByProps("_metrics");
|
||||
|
||||
// Sentry interface definition to avoid TypeScript errors
|
||||
interface SentryClient {
|
||||
getOptions: () => { enabled: boolean };
|
||||
close: () => void;
|
||||
open: () => void;
|
||||
}
|
||||
|
||||
interface SentryScope {
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
interface SentryHub {
|
||||
getClient: () => SentryClient | undefined;
|
||||
getStackTop: () => { scope: SentryScope };
|
||||
getScope: () => SentryScope;
|
||||
addBreadcrumb?: (breadcrumb: unknown) => void;
|
||||
}
|
||||
|
||||
interface GlobalSentry {
|
||||
hub?: SentryHub;
|
||||
}
|
||||
|
||||
// Get Sentry objects with proper type checking
|
||||
const sentryGlobal = (globalThis as unknown as { __SENTRY__?: GlobalSentry })
|
||||
.__SENTRY__;
|
||||
const sentryHub = sentryGlobal?.hub;
|
||||
const sentryClient = sentryHub?.getClient();
|
||||
|
||||
// Sentry related objects with type safety
|
||||
const Sentry = {
|
||||
initializer: findByProps("initSentry"),
|
||||
main: sentryHub,
|
||||
client: sentryClient,
|
||||
};
|
||||
|
||||
// Utility function to create no-operation patches
|
||||
const noop = (prop: string, parent: Record<string, any>): PatchCleanupFn => {
|
||||
try {
|
||||
return instead(prop, parent, () => undefined);
|
||||
} catch (e) {
|
||||
return () => false;
|
||||
}
|
||||
};
|
||||
|
||||
// Network tracking prevention
|
||||
function patchNetwork(): PatchCleanupFn {
|
||||
// Analytics and tracking endpoints to block
|
||||
const analyticsTest =
|
||||
/client-analytics\.braintreegateway\.com|discord\.com\/api\/v9\/(science|track)|app\.adjust\..*|.*\.ingest\.sentry\.io/;
|
||||
|
||||
try {
|
||||
const unpatch = instead(
|
||||
"send",
|
||||
XMLHttpRequest.prototype,
|
||||
function (
|
||||
this: XMLHttpRequest & { __sentry_xhr__?: { url: string } },
|
||||
args: unknown[],
|
||||
orig: Function,
|
||||
) {
|
||||
if (
|
||||
this.__sentry_xhr__?.url &&
|
||||
analyticsTest.test(this.__sentry_xhr__.url)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
return orig.apply(this, args);
|
||||
},
|
||||
);
|
||||
return unpatch;
|
||||
} catch (e) {
|
||||
return () => false;
|
||||
}
|
||||
}
|
||||
|
||||
// Console patching to remove Sentry wrappers
|
||||
function patchConsole(): PatchCleanupFn {
|
||||
type ConsoleFunction = Function & { __sentry_original__?: Function };
|
||||
type ConsoleFunctions = Record<string, ConsoleFunction>;
|
||||
|
||||
const sentrified: ConsoleFunctions = {};
|
||||
|
||||
try {
|
||||
// We need to use Object.keys because TypeScript doesn't allow indexing Console with strings
|
||||
(Object.keys(console) as Array<keyof Console>).forEach((key) => {
|
||||
const consoleFunc = (console as unknown as ConsoleFunctions)[key];
|
||||
if (consoleFunc) {
|
||||
sentrified[key] = consoleFunc;
|
||||
const originalFunc = consoleFunc.__sentry_original__;
|
||||
(console as unknown as ConsoleFunctions)[key] =
|
||||
originalFunc ?? consoleFunc;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
logger.log("Failed to de-sentrify console functions!", e);
|
||||
}
|
||||
|
||||
return () => {
|
||||
Object.keys(sentrified).forEach((key) => {
|
||||
if (sentrified[key]) {
|
||||
(console as unknown as ConsoleFunctions)[key] = sentrified[key];
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Miscellaneous tracking functions
|
||||
function patchMiscellaneous(): PatchCleanupFn {
|
||||
const miscPatches = [
|
||||
// Global analytics utilities
|
||||
AnalyticsUtils?.AnalyticsActionHandlers &&
|
||||
noop("handleTrack", AnalyticsUtils.AnalyticsActionHandlers),
|
||||
AnalyticsUtils?.AnalyticsActionHandlers &&
|
||||
noop("handleFingerprint", AnalyticsUtils.AnalyticsActionHandlers),
|
||||
|
||||
// Super properties tracking
|
||||
SuperPropUtils && noop("track", SuperPropUtils),
|
||||
|
||||
// Voice state metadata tracking
|
||||
VoiceStateUtils && noop("trackWithMetadata", VoiceStateUtils),
|
||||
|
||||
// Crash reporter
|
||||
CrashReportUtils && noop("submitLiveCrashReport", CrashReportUtils),
|
||||
|
||||
// Metrics
|
||||
MetricsUtils?._metrics && noop("push", MetricsUtils._metrics),
|
||||
].filter(Boolean) as PatchCleanupFn[];
|
||||
|
||||
return () => miscPatches.forEach((p) => p());
|
||||
}
|
||||
|
||||
// Sentry error reporting prevention
|
||||
function patchSentry(): PatchCleanupFn {
|
||||
const sentryPatches: PatchCleanupFn[] = [];
|
||||
|
||||
// Add patches if Sentry components exist
|
||||
if (Sentry.initializer) {
|
||||
sentryPatches.push(noop("initSentry", Sentry.initializer));
|
||||
}
|
||||
|
||||
if (Sentry.main && Sentry.main.addBreadcrumb) {
|
||||
sentryPatches.push(noop("addBreadcrumb", Sentry.main));
|
||||
}
|
||||
|
||||
// Disable Sentry client
|
||||
if (Sentry.client) {
|
||||
try {
|
||||
Sentry.client.getOptions().enabled = false;
|
||||
Sentry.client.close();
|
||||
|
||||
if (Sentry.main) {
|
||||
if (Sentry.main.getStackTop) {
|
||||
Sentry.main.getStackTop().scope.clear();
|
||||
}
|
||||
|
||||
if (Sentry.main.getScope) {
|
||||
Sentry.main.getScope().clear();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
try {
|
||||
sentryPatches.forEach((p) => p());
|
||||
|
||||
// Re-enable Sentry client on cleanup
|
||||
if (Sentry.client) {
|
||||
Sentry.client.getOptions().enabled = true;
|
||||
Sentry.client.open();
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail
|
||||
}
|
||||
};
|
||||
}
|
||||
const patches = [
|
||||
network(),
|
||||
console(),
|
||||
miscellanous(),
|
||||
// Sentry causes issues
|
||||
// sentry(),
|
||||
];
|
||||
|
||||
export default defineCorePlugin({
|
||||
manifest: {
|
||||
id: "bunny.notrack",
|
||||
version: "1.0.0",
|
||||
type: "plugin",
|
||||
spec: 3,
|
||||
main: "",
|
||||
display: {
|
||||
name: "NoTrack",
|
||||
description: "Disables Discord's telemetry",
|
||||
authors: [{ name: "maisymoe" }],
|
||||
manifest: {
|
||||
id: "vendetta.notrack",
|
||||
version: "1.0.0",
|
||||
type: "plugin",
|
||||
spec: 3,
|
||||
main: "",
|
||||
display: {
|
||||
name: "NoTrack",
|
||||
description: "Disables Discord's telemetry",
|
||||
authors: [{ name: "maisymoe" }]
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
start() {
|
||||
// Apply all patches
|
||||
patches = [
|
||||
patchNetwork(),
|
||||
patchConsole(),
|
||||
patchMiscellaneous(),
|
||||
patchSentry(),
|
||||
].filter(Boolean);
|
||||
|
||||
logger.log("NoTrack: Enabled - all telemetry tracking disabled");
|
||||
},
|
||||
|
||||
stop() {
|
||||
// Call all cleanup functions
|
||||
patches.forEach((p) => p?.());
|
||||
patches = [];
|
||||
|
||||
logger.log("NoTrack: Disabled - telemetry tracking restored");
|
||||
},
|
||||
start() {
|
||||
network(),
|
||||
console(),
|
||||
miscellanous(),
|
||||
sentry()
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
16
src/core/plugins/notrack/lib/console.ts
Normal file
16
src/core/plugins/notrack/lib/console.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { logger } from "@lib/utils/logger";
|
||||
|
||||
export default () => {
|
||||
const sentrified = {};
|
||||
|
||||
try {
|
||||
Object.keys(console).forEach((x) => {
|
||||
sentrified[x] = console[x];
|
||||
console[x] = console[x].__sentry_original__ ?? console[x];
|
||||
});
|
||||
} catch(e) {
|
||||
logger.log("Failed to de-sentrify console functions!", e);
|
||||
};
|
||||
|
||||
return () => Object.keys(console).forEach((x) => console[x] = sentrified[x]);
|
||||
}
|
||||
33
src/core/plugins/notrack/lib/miscellanous.ts
Normal file
33
src/core/plugins/notrack/lib/miscellanous.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { findByProps } from "@metro";
|
||||
import { noop } from "./utils";
|
||||
|
||||
// Stuff in here is non-specific tracking scattered around the app
|
||||
|
||||
const AnalyticsUtils = findByProps("AnalyticsActionHandlers");
|
||||
const SuperPropUtils = findByProps("encodeProperties", "track");
|
||||
const VoiceStateUtils = findByProps("getVoiceStateMetadata");
|
||||
const CrashReportUtils = findByProps("submitLiveCrashReport");
|
||||
const MetricsUtils = findByProps("_metrics");
|
||||
|
||||
export default () => {
|
||||
const patches = [
|
||||
// Global analytics utilities
|
||||
...(["handleTrack", "handleFingerprint"].map((i) => noop(i, AnalyticsUtils.AnalyticsActionHandlers))),
|
||||
// TODO: This is a getter - look into patching it
|
||||
// noop("analyticsTrackingStoreMaker", AnalyticsUtils),
|
||||
|
||||
// Super properties tracking
|
||||
noop("track", SuperPropUtils),
|
||||
|
||||
// Voice state metadata tracking
|
||||
noop("trackWithMetadata", VoiceStateUtils),
|
||||
|
||||
// Crash reporter
|
||||
noop("submitLiveCrashReport", CrashReportUtils),
|
||||
|
||||
// Whatever "metrics" implies
|
||||
noop("push", MetricsUtils._metrics),
|
||||
];
|
||||
|
||||
return () => patches.forEach((p) => p());
|
||||
}
|
||||
12
src/core/plugins/notrack/lib/network.ts
Normal file
12
src/core/plugins/notrack/lib/network.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { instead } from "@lib/api/patcher";
|
||||
|
||||
export default () => {
|
||||
// https://github.com/yellowsink/shelter-plugins/blob/3a62d1557b704f4617f72882edba826218eb8c6e/plugins/antitrack/index.js#L10-L25
|
||||
const analyticsTest = /client-analytics\.braintreegateway\.com|discord\.com\/api\/v9\/(science|track)|app\.adjust\..*|.*\.ingest\.sentry\.io/;
|
||||
|
||||
return instead(
|
||||
"send",
|
||||
XMLHttpRequest.prototype,
|
||||
function (args, orig) { if (!analyticsTest.test(this.__sentry_xhr__?.url)) return orig.apply(this, args) },
|
||||
);
|
||||
}
|
||||
26
src/core/plugins/notrack/lib/sentry.ts
Normal file
26
src/core/plugins/notrack/lib/sentry.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { findByProps } from "@metro";
|
||||
import { noop } from "./utils";
|
||||
|
||||
const Sentry = {
|
||||
initializer: findByProps("initSentry"),
|
||||
main: globalThis.__SENTRY__?.hub,
|
||||
client: globalThis.__SENTRY__?.hub?.getClient(),
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const patches = [
|
||||
noop("initSentry", Sentry.initializer),
|
||||
noop("addBreadcrumb", Sentry.main),
|
||||
];
|
||||
|
||||
Sentry.client.getOptions().enabled = false;
|
||||
Sentry.client.close();
|
||||
Sentry.main.getStackTop().scope.clear();
|
||||
Sentry.main.getScope().clear();
|
||||
|
||||
return () => {
|
||||
patches.forEach((p) => p());
|
||||
Sentry.client.getOptions().enabled = true;
|
||||
Sentry.client.open();
|
||||
};
|
||||
}
|
||||
3
src/core/plugins/notrack/lib/utils.ts
Normal file
3
src/core/plugins/notrack/lib/utils.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import { instead } from "@lib/api/patcher";
|
||||
|
||||
export const noop = (prop: string, parent: any) => instead(prop, parent, () => {});
|
||||
Loading…
Add table
Reference in a new issue