Skip to content

Automate and spoof Discord quest progress for Play, Stream, and Video activities using the desktop app. Includes script and step-by-step instructions for easy use.

License

Notifications You must be signed in to change notification settings

AswinOP/discord-quest-completer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 

Repository files navigation

Discord Quest Completion Script

This script helps you quickly complete Discord quests (play, stream, watch video, or activity) using the Discord desktop app.

Note:

  • This does not work in browsers for non-video, non-activity quests!
  • For stream/play quests, always use the desktop app.
  • When doing stream quests, you need at least 1 other account in the voice channel (VC).

Features

  • Handles these quest types:
    • Play on Desktop
    • Stream on Desktop
    • Watch Video
    • Play Activity
  • Only works reliably for most quests in the Discord desktop app.

How to Use

  1. Open Discord desktop app.
  2. Go to Discover → Quests and accept a quest.
  3. If pressing Ctrl+Shift+I does not open DevTools, add this line to your %appdata%/discord/settings.json and restart Discord:
"DANGEROUS_ENABLE_DEVTOOLS_ONLY_ENABLE_IF_YOU_KNOW_WHAT_YOURE_DOING": true
  1. Press Ctrl+Shift+I and open the Console tab.
  2. Copy and paste the code below, then press Enter.
  3. For stream quests: ensure at least one other account is present in the VC.

Troubleshooting

  • Script does not work:
    • Confirm you have a quest accepted and uncompleted.
    • Must use the desktop app, not a browser.
  • For stream quests:
    • You need at least one other participant in the voice channel.
  • DevTools won’t open:
    • See step 3 above.

Script

Click to copy code
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 progress for "${questName}".`);
    } else if(taskName === "PLAY_ON_DESKTOP") {
        if(!isApp) {
            console.log("This does not work in the browser for non-video quests. Please 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 approximately ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`);
            });
        }
    } else if(taskName === "STREAM_ON_DESKTOP") {
        if(!isApp) {
            console.log("This does not work in the 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 a voice channel for about ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`);
            console.log("Ensure at least one other person is in the voice channel!");
        }
    } 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();
    }
}

About

Automate and spoof Discord quest progress for Play, Stream, and Video activities using the desktop app. Includes script and step-by-step instructions for easy use.

Topics

Resources

License

Stars

Watchers

Forks