Disclaimer This project is provided for educational and research purposes only. It demonstrates how time based Discord quest progress is reported from the client. Using this code on live services may violate platform Terms of Service and may result in penalties.
This project is a lightweight proof of concept that simulates progress for Discord quests by sending gradual, time respecting progress updates.
The implementation is intentionally minimal and focuses on:
- Realistic timing
- Incremental progress
- Avoiding instant or abnormal completion patterns
- Finds an active, enrolled, incomplete quest
- Reads the quest’s required watch duration
- Gradually advances progress using small randomized steps
- Ensures progress never exceeds reasonable time bounds
- Stops automatically once the target is reached
No UI manipulation, no auto-accept, no auto-claim logic.
- Enable Developer Tools inside discord. I recommend the PTB version of Discord when doing this
- Press Ctrl + Shift + I (or the equivalent shortcut on your system).
- Navigate to the Console tab.
- Paste the script into the console and press Enter.
Progress is simulated using the following constraints:
-
Enrollment time aware
- Progress is capped relative to how much real time has passed since enrollment
-
Randomized increments
- Each update advances by a small, non-uniform amount
-
Human like pacing
- Randomized delays are applied between updates
This produces a progress curve that closely resembles legitimate playback behavior.
delete window.$;
const wpModules = webpackChunkdiscord_app.push([[Symbol()], {}, r => r]);
webpackChunkdiscord_app.pop();
const StreamingStore = Object.values(wpModules.c).find(m => m?.exports?.Z?.__proto__?.getStreamerActiveStreamMetadata)?.exports?.Z;
const GamesStore = Object.values(wpModules.c).find(m => m?.exports?.ZP?.getRunningGames)?.exports?.ZP;
const Quests = Object.values(wpModules.c).find(m => m?.exports?.Z?.__proto__?.getQuest)?.exports?.Z;
const ThreadStore = Object.values(wpModules.c).find(m => m?.exports?.Z?.__proto__?.getAllThreadsForParent)?.exports?.Z;
const GuildStore = Object.values(wpModules.c).find(m => m?.exports?.ZP?.getSFWDefaultChannel)?.exports?.ZP;
const Dispatcher = Object.values(wpModules.c).find(m => m?.exports?.Z?.__proto__?.flushWaitQueue)?.exports?.Z;
const clientApi = Object.values(wpModules.c).find(m => m?.exports?.tn?.get)?.exports?.tn;
const taskTypes = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY", "WATCH_VIDEO_ON_MOBILE"];
let pendingQuests = [...Quests.quests.values()].filter(q =>
q.userStatus?.enrolledAt &&
!q.userStatus?.completedAt &&
new Date(q.config.expiresAt).getTime() > Date.now() &&
taskTypes.some(t => Object.keys((q.config.taskConfig ?? q.config.taskConfigV2).tasks).includes(t))
);
const isDesktopApp = typeof DiscordNative !== "undefined";
if (!pendingQuests.length) {
console.log("No uncompleted quests available!");
} else {
const runQuest = () => {
const quest = pendingQuests.pop();
if (!quest) return;
const randomPid = Math.floor(Math.random() * 30000) + 1000;
const appId = quest.config.application?.id;
const appName = quest.config.application?.name;
const questTitle = quest.config.messages?.questName;
const config = quest.config.taskConfig ?? quest.config.taskConfigV2;
const currentTask = taskTypes.find(t => config.tasks[t] != null);
const requiredSeconds = config.tasks[currentTask].target;
let completedSeconds = quest.userStatus?.progress?.[currentTask]?.value ?? 0;
if (currentTask === "WATCH_VIDEO" || currentTask === "WATCH_VIDEO_ON_MOBILE") {
const intervalSec = 1;
const speed = 7;
const buffer = 10;
const enrolledTime = new Date(quest.userStatus.enrolledAt).getTime();
let finished = false;
(async function updateVideoProgress() {
while (completedSeconds < requiredSeconds) {
const allowed = Math.floor((Date.now() - enrolledTime) / 1000) + buffer;
const nextSeconds = completedSeconds + speed;
if (allowed - completedSeconds >= speed) {
const response = await clientApi.post({
url: `/quests/${quest.id}/video-progress`,
body: { timestamp: Math.min(requiredSeconds, nextSeconds + Math.random()) }
});
finished = response.body.completed_at != null;
completedSeconds = Math.min(requiredSeconds, nextSeconds);
}
if (nextSeconds >= requiredSeconds) break;
await new Promise(r => setTimeout(r, intervalSec * 1000));
}
if (!finished) {
await clientApi.post({ url: `/quests/${quest.id}/video-progress`, body: { timestamp: requiredSeconds } });
}
console.log(`Quest "${questTitle}" completed!`);
runQuest();
})();
console.log(`Simulating video watch for "${questTitle}"...`);
}
else if (currentTask === "PLAY_ON_DESKTOP") {
if (!isDesktopApp) {
console.log("Non-video quests require the Discord desktop app for:", questTitle);
return;
}
clientApi.get({ url: `/applications/public?application_ids=${appId}` }).then(res => {
const appData = res.body[0];
const exe = appData.executables.find(x => x.os === "win32").name.replace(">", "");
const fakeProcess = {
cmdLine: `C:\\Program Files\\${appData.name}\\${exe}`,
exeName: exe,
exePath: `c:/program files/${appData.name.toLowerCase()}/${exe}`,
hidden: false,
isLauncher: false,
id: appId,
name: appData.name,
pid: randomPid,
pidPath: [randomPid],
processName: appData.name,
start: Date.now()
};
const originalGames = GamesStore.getRunningGames();
const fakeGames = [fakeProcess];
const oldGetGames = GamesStore.getRunningGames;
const oldGetGameForPID = GamesStore.getGameForPID;
GamesStore.getRunningGames = () => fakeGames;
GamesStore.getGameForPID = (pid) => fakeGames.find(x => x.pid === pid);
Dispatcher.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: originalGames, added: [fakeProcess], games: fakeGames });
const progressHandler = data => {
const progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value);
console.log(`Quest progress: ${progress}/${requiredSeconds}`);
if (progress >= requiredSeconds) {
console.log(`Quest "${questTitle}" completed!`);
GamesStore.getRunningGames = oldGetGames;
GamesStore.getGameForPID = oldGetGameForPID;
Dispatcher.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: [fakeProcess], added: [], games: [] });
Dispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", progressHandler);
runQuest();
}
};
Dispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", progressHandler);
console.log(`Spoofed game to "${appName}". Remaining: ~${Math.ceil((requiredSeconds - completedSeconds) / 60)} minutes.`);
});
}
// STREAM_ON_DESKTOP and PLAY_ACTIVITY can be added similarly
};
runQuest();
}
thanks
works perfectly