const cache = {};
if (typeof DiscordNative === 'undefined') {
console.log(
'[rpc-spoofer] Browser is not supported. Please use Discord client :wilted_rose:.',
);
} else {
const req = webpackChunkdiscord_app.push([[Symbol()], {}, (r) => r]);
webpackChunkdiscord_app.pop();
const mods = Object.values(req.c);
const api = mods.find((x) => x?.exports?.Bo?.get)?.exports?.Bo;
const store = mods.find(
(x) => x?.exports?.Ay?.getRunningGames,
)?.exports?.Ay;
const dispatcher = mods.find(
(x) => x?.exports?.h?.__proto__?.flushWaitQueue,
)?.exports?.h;
if (!api || !store || !dispatcher) {
throw new Error(
'[rpc-spoofer] Failed to locate Discord internals.',
);
}
const startedAt = Date.now();
const log = (...args) =>
console.log(
`[rpc-spoofer +${((Date.now() - startedAt) / 1000).toFixed(1)}s]`,
...args,
);
const error = (...args) =>
console.error(
`[rpc-spoofer +${((Date.now() - startedAt) / 1000).toFixed(1)}s]`,
...args,
);
log('Initializing...');
log('Fetching detectable applications...');
api.get('/applications/detectable')
.then(async ({ body: apps }) => {
log(`Received ${apps.length} detectable applications.`);
const targetCount = Math.min(100, apps.length);
while (Object.keys(cache).length < targetCount) {
const app = apps[(Math.random() * apps.length) | 0];
if (app?.id) {
cache[app.id] = app;
}
}
const ids = Object.keys(cache);
log(`Cached ${ids.length} unique applications.`);
let i = 0;
let switchTimer = null;
let heartbeatTimer = null;
let nextSwitchAt = null;
const cleanup = () => {
const state = window.__rpcSpooferState;
if (!state) {
log('No active spoof state to clean up.');
return;
}
log(
`Removing spoofed game: ${state.game.name} (${state.game.id})`,
);
state.store.getRunningGames = state.getGames;
state.store.getGameForPID = state.getPID;
state.dispatcher.dispatch({
type: 'RUNNING_GAMES_CHANGE',
removed: [state.game],
added: [],
games: state.getGames(),
});
delete window.__rpcSpooferState;
log('Original RunningGameStore methods restored.');
};
const heartbeat = () => {
const state = window.__rpcSpooferState;
if (!state) {
log('HEARTBEAT | idle');
return;
}
const elapsed = Date.now() - state.game.start;
const nextSwitch =
nextSwitchAt === null
? 'none'
: `${Math.max(
0,
Math.ceil((nextSwitchAt - Date.now()) / 1000),
)}s`;
let games;
try {
games = state.store.getRunningGames();
} catch (e) {
games = [];
error('Failed to read running games during heartbeat:', e);
}
log(
`HEARTBEAT | game="${state.game.name}"` +
` id=${state.game.id}` +
` pid=${state.game.pid}` +
` alive=${games.some((x) => x?.pid === state.game.pid)}` +
` uptime=${Math.floor(elapsed / 1000)}s` +
` next=${nextSwitch}` +
` progress=${i}/${ids.length}`,
);
};
const spoof = async () => {
cleanup();
if (i >= ids.length) {
if (switchTimer) clearTimeout(switchTimer);
if (heartbeatTimer) clearInterval(heartbeatTimer);
log('All cached applications have been spoofed.');
return;
}
const id = ids[i];
log(
`Fetching application ${id} (${i + 1}/${ids.length})...`,
);
try {
const requestStarted = performance.now();
const { body } = await api.get({
url: `/applications/public?application_ids=${id}`,
});
log(
`Application request completed in ${(
performance.now() - requestStarted
).toFixed(1)}ms.`,
);
const app = body[0];
if (!app) {
error(`Cannot fetch application ${id}.`);
i++;
scheduleNext();
return;
}
const pid = (Math.random() * 3e4 + 1e3) | 0;
const exe =
app.executables
?.find((x) => x.os === 'win32')
?.name?.replace('>', '') ||
app.name.replace(/[\/\\:*?"<>|]/g, '');
const game = {
cmdLine: `C:\\Program Files\\${app.name}\\${exe}`,
exeName: exe,
exePath: `c:/program files/${app.name.toLowerCase()}/${exe}`,
hidden: false,
isLauncher: false,
id,
name: app.name,
pid,
pidPath: [pid],
processName: app.name,
start: Date.now(),
};
const realGames = store.getRunningGames();
log(
`Current real games: ${
realGames.length
? realGames
.map((x) => `${x.name} (${x.pid})`)
.join(', ')
: 'none'
}`,
);
window.__rpcSpooferState = {
store,
dispatcher,
game,
getGames: store.getRunningGames,
getPID: store.getGameForPID,
};
store.getRunningGames = () => [game];
store.getGameForPID = (requestedPid) =>
requestedPid === pid ? game : undefined;
log('Dispatching RUNNING_GAMES_CHANGE...', {
removed: realGames.map((x) => ({
name: x.name,
pid: x.pid,
})),
added: [
{
name: game.name,
pid: game.pid,
},
],
});
dispatcher.dispatch({
type: 'RUNNING_GAMES_CHANGE',
removed: realGames,
added: [game],
games: [game],
});
i++;
log(
`SPOOF ACTIVE | Playing: ${app.name} (${id}) | PID ${pid}`,
);
log(`Executable: ${exe}`);
log(`Path: ${game.exePath}`);
heartbeat();
scheduleNext();
} catch (e) {
error(`Spoof error for application ${id}:`, e);
i++;
scheduleNext();
}
};
const scheduleNext = () => {
if (i >= ids.length) {
nextSwitchAt = null;
switchTimer = setTimeout(spoof, 0);
return;
}
const delay = ((Math.random() * 2) | 0) + 1;
const delayMs = delay * 61_000;
nextSwitchAt = Date.now() + delayMs;
log(
`Next application switch in ${delayMs / 1000}s at ${new Date(
nextSwitchAt,
).toLocaleTimeString()}.`,
);
switchTimer = setTimeout(spoof, delayMs);
};
heartbeatTimer = setInterval(heartbeat, 15_000);
window.__stopRpcSpoofer = () => {
log('Manual stop requested.');
if (switchTimer) clearTimeout(switchTimer);
if (heartbeatTimer) clearInterval(heartbeatTimer);
cleanup();
log('Stopped.');
};
log('Heartbeat interval: 15s');
log('Use __stopRpcSpoofer() to stop and restore the store.');
spoof();
})
.catch((e) => {
error('Failed to fetch detectable applications:', e);
});
}