Skip to content

Instantly share code, notes, and snippets.

@nadidazwad
Forked from aamiaa/CompleteDiscordQuest.md
Last active January 10, 2026 18:40
Show Gist options
  • Select an option

  • Save nadidazwad/0feacefd4b27c35d321ac12309df5966 to your computer and use it in GitHub Desktop.

Select an option

Save nadidazwad/0feacefd4b27c35d321ac12309df5966 to your computer and use it in GitHub Desktop.
Complete Recent Discord Quest

Complete Discord Quest (Patched)

Based on aamiaa/CompleteDiscordQuest.md

This fork fixes the video quest 400 Bad Request error by adding increment_num field and using integer timestamps.

How to use

  1. Accept a quest under Discover → Quests
  2. Press Ctrl+Shift+I to open DevTools
  3. Go to the Console tab
  4. Paste the script below and press Enter
  5. Follow the console instructions
Click to expand script
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;

const supportedTasks = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY", "WATCH_VIDEO_ON_MOBILE"]
let quests = [...QuestsStore.quests.values()].filter(x => x.userStatus?.enrolledAt && !x.userStatus?.completedAt && new Date(x.config.expiresAt).getTime() > Date.now() && supportedTasks.find(y => Object.keys((x.config.taskConfig ?? x.config.taskConfigV2).tasks).includes(y)))
let isApp = typeof DiscordNative !== "undefined"
if (quests.length === 0) {
    console.log("You don't have any uncompleted quests!")
} else {
    let doJob = function () {
        const quest = quests.pop()
        if (!quest) return

        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 = supportedTasks.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 incrementNum = Math.floor(secondsDone / speed)
            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 body = {
                            timestamp: Math.floor(Math.min(secondsNeeded, timestamp)),
                            increment_num: incrementNum
                        }
                        const res = await api.post({ url: `/quests/${quest.id}/video-progress`, body })
                        completed = res.body.completed_at != null
                        secondsDone = Math.min(secondsNeeded, timestamp)
                        incrementNum++
                        console.log(`Quest progress: ${secondsDone}/${secondsNeeded}`)
                    }
                    if (timestamp >= secondsNeeded) break
                    await new Promise(resolve => setTimeout(resolve, interval * 1000))
                }
                if (!completed) {
                    const body = {
                        timestamp: Math.floor(secondsNeeded),
                        increment_num: incrementNum
                    }
                    await api.post({ url: `/quests/${quest.id}/video-progress`, body })
                }
                console.log("Quest completed!")
                doJob()
            }
            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 discord 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)
                            doJob()
                        }
                    }
                    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 discord 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)
                        doJob()
                    }
                }
                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!")
                doJob()
            }
            fn()
        }
    }
    doJob()
}

What's Fixed

  • Video quests now work without 400 errors
    • Added increment_num field to POST body
    • Changed timestamps to integers (removed Math.random())

Notes

  • Video quests complete in seconds
  • Video quest UI may not update, the progress bar might stay at 0%, but the quest is progressing. Check the console for actual progress. It will jump to 100% when complete.
  • Gameplay/streaming quests still take ~15 minutes (uses Discord's heartbeat system)
  • Don't use in browser for non-video quests, requires the ptb desktop app

Credits

Original script by aamiaa

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment