Note
This does not work in the browser for quests which require you to play a game! Use the desktop app to complete those.
- Current: 0.0.2
- Previous: 0.0.1
- Added fallback module detection - Now checks for both .Z/.ZP/.tn and .A/.Ay/.Bo export patterns
- Added optional chaining (?.) - Prevents crashes when modules aren't found
- Added task type filtering - The original default code filters quests by valid task types, which was missing
- Fixed variable naming - Aligned variable names with their purposes
- Initial release
How to use this script:
- Accept your quests under Discover -> Quests.
- Press Ctrl+Shift+I to open DevTools
- Go to the
Consoletab. - Paste the following code and hit enter:
Click to expand
delete window.$;
let e = webpackChunkdiscord_app.push([[Symbol()], {}, e => e]);
webpackChunkdiscord_app.pop();
let t, s, a, o, n, r, i;
// Check which export format Discord is using
let testModule = Object.values(e.c).find((e => e?.exports?.Z?.__proto__?.getStreamerActiveStreamMetadata))?.exports?.Z;
if (testModule) {
// First format (.Z, .ZP, .tn)
t = testModule;
s = Object.values(e.c).find((e => e?.exports?.ZP?.getRunningGames))?.exports?.ZP;
a = Object.values(e.c).find((e => e?.exports?.Z?.__proto__?.getQuest))?.exports?.Z;
o = Object.values(e.c).find((e => e?.exports?.Z?.__proto__?.getAllThreadsForParent))?.exports?.Z;
n = Object.values(e.c).find((e => e?.exports?.ZP?.getSFWDefaultChannel))?.exports?.ZP;
r = Object.values(e.c).find((e => e?.exports?.Z?.__proto__?.flushWaitQueue))?.exports?.Z;
i = Object.values(e.c).find((e => e?.exports?.tn?.get))?.exports?.tn;
} else {
// Fallback format (.A, .Ay, .Bo)
t = Object.values(e.c).find((e => e?.exports?.A?.__proto__?.getStreamerActiveStreamMetadata))?.exports?.A;
s = Object.values(e.c).find((e => e?.exports?.Ay?.getRunningGames))?.exports?.Ay;
a = Object.values(e.c).find((e => e?.exports?.A?.__proto__?.getQuest))?.exports?.A;
o = Object.values(e.c).find((e => e?.exports?.A?.__proto__?.getAllThreadsForParent))?.exports?.A;
n = Object.values(e.c).find((e => e?.exports?.Ay?.getSFWDefaultChannel))?.exports?.Ay;
r = Object.values(e.c).find((e => e?.exports?.h?.__proto__?.flushWaitQueue))?.exports?.h;
i = Object.values(e.c).find((e => e?.exports?.Bo?.get))?.exports?.Bo;
}
const taskTypes = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY", "WATCH_VIDEO_ON_MOBILE"];
let l = [...a.quests.values()].filter((e =>
e.userStatus?.enrolledAt &&
!e.userStatus?.completedAt &&
new Date(e.config.expiresAt).getTime() > Date.now() &&
taskTypes.find((t => Object.keys((e.config.taskConfig ?? e.config.taskConfigV2).tasks).includes(t)))
));
let d = "undefined" != typeof DiscordNative;
if (0 === l.length) {
console.log("You don't have any uncompleted quests!");
} else {
console.log(`Found ${l.length} uncompleted quest(s). Starting parallel processing...\n`);
const getTaskName = (e) => {
const t = e.config.taskConfig ?? e.config.taskConfigV2;
return taskTypes.find((e => null != t.tasks[e]));
};
const origGetRunningGames = s.getRunningGames;
const origGetGameForPID = s.getGameForPID;
const origGetStreamerMeta = t.getStreamerActiveStreamMetadata;
let fakeGames = [];
let activeQuests = new Set();
let completedQuests = new Set();
const cleanup = () => {
if (activeQuests.size === 0) {
console.log("\n๐งน Cleaning up...");
s.getRunningGames = origGetRunningGames;
s.getGameForPID = origGetGameForPID;
t.getStreamerActiveStreamMetadata = origGetStreamerMeta;
r.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: fakeGames, added: [], games: [] });
console.log("๐ All quests completed!");
}
};
const playQuests = l.filter((e => "PLAY_ON_DESKTOP" === getTaskName(e)));
const setupPlayQuests = async () => {
if (!d || playQuests.length === 0) return [];
const results = [];
for (const quest of playQuests) {
const fakePid = Math.floor(30000 * Math.random()) + 1000 + results.length;
const appId = quest.config.application.id;
try {
const response = await i.get({ url: `/applications/public?application_ids=${appId}` });
const app = response.body[0];
const exeName = app.executables?.find((e => "win32" === e.os))?.name?.replace(">", "") || "game.exe";
const fakeGame = {
cmdLine: `C:\\Program Files\\${app.name}\\${exeName}`,
exeName: exeName,
exePath: `c:/program files/${app.name.toLowerCase()}/${exeName}`,
hidden: false,
isLauncher: false,
id: appId,
name: app.name,
pid: fakePid,
pidPath: [fakePid],
processName: app.name,
start: Date.now()
};
results.push({ quest, fakeGame, pid: fakePid });
fakeGames.push(fakeGame);
} catch (err) {
console.log(`[PLAY: ${quest.config.messages.questName}] Error setting up: ${err.message}`);
}
}
if (fakeGames.length > 0) {
s.getRunningGames = () => fakeGames;
s.getGameForPID = (pid) => fakeGames.find((g => g.pid === pid));
r.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: [], added: fakeGames, games: fakeGames });
console.log(`๐ฎ Spoofed ${fakeGames.length} games: ${fakeGames.map((g => g.name)).join(", ")}\n`);
}
return results;
};
const processQuest = async (quest, setupInfo = null) => {
const questId = quest.id;
const questName = quest.config.messages.questName;
const taskName = getTaskName(quest);
const taskConfig = (quest.config.taskConfig ?? quest.config.taskConfigV2).tasks[taskName];
const target = taskConfig.target;
const log = (msg) => console.log(`[${questName}] ${msg}`);
activeQuests.add(questId);
const getProgress = () => {
const q = [...a.quests.values()].find((e => e.id === questId));
if (!q) return 0;
if (taskName === "PLAY_ON_DESKTOP") {
return q.config.configVersion === 1
? (q.userStatus?.streamProgressSeconds ?? 0)
: Math.floor(q.userStatus?.progress?.PLAY_ON_DESKTOP?.value ?? 0);
}
if (taskName === "STREAM_ON_DESKTOP") {
return q.config.configVersion === 1
? (q.userStatus?.streamProgressSeconds ?? 0)
: Math.floor(q.userStatus?.progress?.STREAM_ON_DESKTOP?.value ?? 0);
}
return q.userStatus?.progress?.[taskName]?.value ?? 0;
};
let progress = getProgress();
log(`Starting... Task: ${taskName}, Progress: ${progress}/${target}s`);
if (taskName === "WATCH_VIDEO" || taskName === "WATCH_VIDEO_ON_MOBILE") {
const increment = 7;
const delay = 1;
const enrolledAt = new Date(quest.userStatus.enrolledAt).getTime();
let completed = false;
while (!completedQuests.has(questId)) {
const elapsed = Math.floor((Date.now() - enrolledAt) / 1000) + 10;
const newProgress = progress + increment;
if (elapsed - progress >= increment) {
try {
const res = await i.post({
url: `/quests/${questId}/video-progress`,
body: { timestamp: Math.min(target, newProgress + Math.random()) }
});
completed = res.body.completed_at != null;
progress = Math.min(target, newProgress);
log(`Progress: ${Math.floor(progress)}/${target}s`);
if (completed) {
completedQuests.add(questId);
break;
}
} catch (err) {
log(`Error: ${err.message}`);
}
}
if (newProgress >= target) break;
await new Promise((res) => setTimeout(res, delay * 1000));
}
if (!completed && !completedQuests.has(questId)) {
await i.post({ url: `/quests/${questId}/video-progress`, body: { timestamp: target } });
}
completedQuests.add(questId);
log("โ Completed!");
} else if (taskName === "PLAY_ON_DESKTOP") {
if (!d) {
log("โ Requires desktop app - skipping");
activeQuests.delete(questId);
return;
}
log(`Waiting for progress... (${Math.ceil((target - progress) / 60)} min remaining)`);
await new Promise((resolve) => {
const checkProgress = () => {
const newProg = getProgress();
if (newProg !== progress) {
progress = newProg;
log(`Progress: ${newProg}/${target}s`);
}
if (newProg >= target) {
completedQuests.add(questId);
log("โ Completed!");
r.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", checkProgress);
resolve();
}
};
r.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", checkProgress);
});
} else if (taskName === "STREAM_ON_DESKTOP") {
if (!d) {
log("โ Requires desktop app - skipping");
activeQuests.delete(questId);
return;
}
const fakePid = Math.floor(30000 * Math.random()) + 1000;
const appId = quest.config.application.id;
t.getStreamerActiveStreamMetadata = () => ({ id: appId, pid: fakePid, sourceName: null });
log(`Stream spoofed to: ${quest.config.application.name}`);
log("โ Stream any window in VC with 1+ other person!");
await new Promise((resolve) => {
const checkProgress = () => {
const newProg = getProgress();
if (newProg !== progress) {
progress = newProg;
log(`Progress: ${newProg}/${target}s`);
}
if (newProg >= target) {
completedQuests.add(questId);
log("โ Completed!");
r.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", checkProgress);
resolve();
}
};
r.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", checkProgress);
});
} else if (taskName === "PLAY_ACTIVITY") {
const channelId = o.getSortedPrivateChannels()[0]?.id ??
Object.values(n.getAllGuilds()).find((g => g?.VOCAL?.length > 0))?.VOCAL[0]?.channel?.id;
if (!channelId) {
log("โ No valid channel found - skipping");
activeQuests.delete(questId);
return;
}
const streamKey = `call:${channelId}:1`;
while (!completedQuests.has(questId)) {
try {
const res = await i.post({
url: `/quests/${questId}/heartbeat`,
body: { stream_key: streamKey, terminal: false }
});
const prog = res.body.progress.PLAY_ACTIVITY.value;
log(`Progress: ${prog}/${target}s`);
if (prog >= target) {
await i.post({
url: `/quests/${questId}/heartbeat`,
body: { stream_key: streamKey, terminal: true }
});
completedQuests.add(questId);
break;
}
} catch (err) {
log(`Error: ${err.message}`);
}
await new Promise((res) => setTimeout(res, 20000));
}
log("โ Completed!");
}
activeQuests.delete(questId);
cleanup();
};
(async () => {
const setupResults = await setupPlayQuests();
const promises = l.map((quest) => {
const setupInfo = setupResults.find((s => s.quest.id === quest.id));
return processQuest(quest, setupInfo);
});
await Promise.all(promises);
})();
}Unlike previous versions of this script which only completed one quest at a time, this updated script grabs all available quests and completes them simultaneously.
The Logic Change:
- let quest = [...QuestsStore.quests.values()].find(x => ...
+ let quests = [...QuestsStore.quests.values()].filter(x => ...
- if (!quest) { ... } else { ... }
+ quests.forEach(quest => { ... })-
Follow the printed instructions depending on the quest type:
- If your quest says to "play" the game or watch a video, you can just wait and do nothing.
- If your quest says to "stream" the game, join a VC with a friend or alt and stream any window.
-
Wait for the script to cycle through and complete all valid quests.
-
You can now claim your rewards!
Q: Can I get banned for using this?
A: There is always a risk, though so far nobody has been banned for this or other similar things like client mods.
Q: Ctrl + Shift + I doesn't work
A: Either download the ptb client, or use this to enable DevTools on stable.
Q: Ctrl + Shift + I takes a screenshot
A: Disable the keybind in your AMD Radeon app.
Q: I get a syntax error/unexpected token error
A: Make sure your browser isn't auto-translating this website before copying the script. Turn off any translator extensions and try again.
Q: I'm on Vesktop but it tells me that I'm using a browser
A: Vesktop is not a true desktop client, it's a fancy browser wrapper. Download the actual desktop app instead.
Q: I get a different error
A: Make sure you're copy/pasting the script correctly and that you've have done all the steps.
Q: Can I complete expired quests with this?
A: No, there is no way to do that.
Q: Can you make the script auto accept the quest/reward?
A: No. Both of those actions may show a captcha, so automating them is not a good idea. Just do the two clicks yourself.
Q: Can you make this a Vencord plugin?
A: No. The script sometimes requires immediate updates for Discord's changes, and Vencord's update cycle and code review would be too slow for that. There are some Vencord forks which have implemented this script or their own quest completers if you really want one.
Q: Can you upload the standalone script to a repo and make this gist's code a one line fetch()?
A: No. Doing that would put you at risk because I (or someone in my account) could change the underlying code to be malicious at any time, then forcepush it away later, and you'd never know.
Click to expand
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
...