Skip to Content
Beta
DiscordComplete Discord Quests

Complete Discord Quest

This does not work in the browser for non-video, non-activity quests! For stream/play quests, use the desktop app!

When doing stream quests, you need at least 1 other account in the VC!

Accept a quest under Discover -> Quests

Press Ctrl+Shift+I to open DevTools

Go to the Console tab

Paste the following code and hit enter:

delete window.$; let wpRequire = webpackChunkdiscord_app.push([[Symbol()], {}, r => r]); webpackChunkdiscord_app.pop(); let ApplicationStreamingStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.getStreamerActiveStreamMetadata).exports.Z; let RunningGameStore = Object.values(wpRequire.c).find(x => x?.exports?.ZP?.getRunningGames).exports.ZP; let QuestsStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.getQuest).exports.Z; let ChannelStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.getAllThreadsForParent).exports.Z; let GuildChannelStore = Object.values(wpRequire.c).find(x => x?.exports?.ZP?.getSFWDefaultChannel).exports.ZP; let FluxDispatcher = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.flushWaitQueue).exports.Z; let api = Object.values(wpRequire.c).find(x => x?.exports?.tn?.get).exports.tn; let quest = [...QuestsStore.quests.values()].find(x => x.id !== "1248385850622869556" && x.userStatus?.enrolledAt && !x.userStatus?.completedAt && new Date(x.config.expiresAt).getTime() > Date.now()) let isApp = typeof DiscordNative !== "undefined" if(!quest) { console.log("You don't have any uncompleted quests!") } else { const pid = Math.floor(Math.random() * 30000) + 1000 const applicationId = quest.config.application.id const applicationName = quest.config.application.name const questName = quest.config.messages.questName const taskConfig = quest.config.taskConfig ?? quest.config.taskConfigV2 const taskName = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY", "WATCH_VIDEO_ON_MOBILE"].find(x => taskConfig.tasks[x] != null) const secondsNeeded = taskConfig.tasks[taskName].target let secondsDone = quest.userStatus?.progress?.[taskName]?.value ?? 0 if(taskName === "WATCH_VIDEO" || taskName === "WATCH_VIDEO_ON_MOBILE") { const maxFuture = 10, speed = 7, interval = 1 const enrolledAt = new Date(quest.userStatus.enrolledAt).getTime() let completed = false let fn = async () => { while(true) { const maxAllowed = Math.floor((Date.now() - enrolledAt)/1000) + maxFuture const diff = maxAllowed - secondsDone const timestamp = secondsDone + speed if(diff >= speed) { const res = await api.post({url: `/quests/${quest.id}/video-progress`, body: {timestamp: Math.min(secondsNeeded, timestamp + Math.random())}}) completed = res.body.completed_at != null secondsDone = Math.min(secondsNeeded, timestamp) } if(timestamp >= secondsNeeded) { break } await new Promise(resolve => setTimeout(resolve, interval * 1000)) } if(!completed) { await api.post({url: `/quests/${quest.id}/video-progress`, body: {timestamp: secondsNeeded}}) } console.log("Quest completed!") } fn() console.log(`Spoofing video for ${questName}.`) } else if(taskName === "PLAY_ON_DESKTOP") { if(!isApp) { console.log("This no longer works in browser for non-video quests. Use the desktop app to complete the", questName, "quest!") } else { api.get({url: `/applications/public?application_ids=${applicationId}`}).then(res => { const appData = res.body[0] const exeName = appData.executables.find(x => x.os === "win32").name.replace(">","") const fakeGame = { cmdLine: `C:\\Program Files\\${appData.name}\\${exeName}`, exeName, exePath: `c:/program files/${appData.name.toLowerCase()}/${exeName}`, hidden: false, isLauncher: false, id: applicationId, name: appData.name, pid: pid, pidPath: [pid], processName: appData.name, start: Date.now(), } const realGames = RunningGameStore.getRunningGames() const fakeGames = [fakeGame] const realGetRunningGames = RunningGameStore.getRunningGames const realGetGameForPID = RunningGameStore.getGameForPID RunningGameStore.getRunningGames = () => fakeGames RunningGameStore.getGameForPID = (pid) => fakeGames.find(x => x.pid === pid) FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: realGames, added: [fakeGame], games: fakeGames}) let fn = data => { let progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value) console.log(`Quest progress: ${progress}/${secondsNeeded}`) if(progress >= secondsNeeded) { console.log("Quest completed!") RunningGameStore.getRunningGames = realGetRunningGames RunningGameStore.getGameForPID = realGetGameForPID FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: [fakeGame], added: [], games: []}) FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn) } } FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn) console.log(`Spoofed your game to ${applicationName}. Wait for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`) }) } } else if(taskName === "STREAM_ON_DESKTOP") { if(!isApp) { console.log("This no longer works in browser for non-video quests. Use the desktop app to complete the", questName, "quest!") } else { let realFunc = ApplicationStreamingStore.getStreamerActiveStreamMetadata ApplicationStreamingStore.getStreamerActiveStreamMetadata = () => ({ id: applicationId, pid, sourceName: null }) let fn = data => { let progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.STREAM_ON_DESKTOP.value) console.log(`Quest progress: ${progress}/${secondsNeeded}`) if(progress >= secondsNeeded) { console.log("Quest completed!") ApplicationStreamingStore.getStreamerActiveStreamMetadata = realFunc FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn) } } FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn) console.log(`Spoofed your stream to ${applicationName}. Stream any window in vc for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`) console.log("Remember that you need at least 1 other person to be in the vc!") } } else if(taskName === "PLAY_ACTIVITY") { const channelId = ChannelStore.getSortedPrivateChannels()[0]?.id ?? Object.values(GuildChannelStore.getAllGuilds()).find(x => x != null && x.VOCAL.length > 0).VOCAL[0].channel.id const streamKey = `call:${channelId}:1` let fn = async () => { console.log("Completing quest", questName, "-", quest.config.messages.questName) while(true) { const res = await api.post({url: `/quests/${quest.id}/heartbeat`, body: {stream_key: streamKey, terminal: false}}) const progress = res.body.progress.PLAY_ACTIVITY.value console.log(`Quest progress: ${progress}/${secondsNeeded}`) await new Promise(resolve => setTimeout(resolve, 20 * 1000)) if(progress >= secondsNeeded) { await api.post({url: `/quests/${quest.id}/heartbeat`, body: {stream_key: streamKey, terminal: true}}) break } } console.log("Quest completed!") } fn() } }

Follow the printed instructions depending on what type of quest you have

  • If your quest says to “play” the game, 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 15 minutes

You can now claim the reward in User Settings -> Gift Inventory!

You can track the progress by looking at the Quest progress: prints in the Console tab, or by reopening the Gift Inventory tab in settings.

FAQ

Q: Ctrl + Shift + I doesn’t work

A: Either download the ptb client, or use this to enable DevTools on stable

Q: I get an error saying “Unauthorized”

A: Discord has patched the script from working in browsers. Use the desktop app.

They have also started checking how many people are in the vc, so make sure you join it on at least 1 other account.

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 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 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 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.

Last updated on