-
-
Save luluwaffless/a1d9479c28a4d31b3bf880ad26e7482b to your computer and use it in GitHub Desktop.
Complete Discord Quests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * This is a fork by luluwaffless (https://github.com/luluwaffless) of | |
| * CompleteDiscordQuest by aamiaa (https://gist.github.com/aamiaa/) with | |
| * support for finishing all uncompleted quests without having to re-run | |
| * the script multiple times. All rights to Amia. Make sure to read the | |
| * document of the original repository if you need help. | |
| * Revision: https://gist.githubusercontent.com/aamiaa/204cd9d42013ded9faf646fae7f89fbb/raw/4912415839790240d49c1d2553e940f0c65f95d5/CompleteDiscordQuest.md | |
| */ | |
| 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 isApp = typeof DiscordNative !== "undefined"; | |
| const sleep = s => new Promise(r => setTimeout(r, s * 1000)); | |
| const completeQuest = quest => new Promise(async (r) => { | |
| 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; | |
| console.log(`Spoofing video for ${questName}.`); | |
| 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 sleep(interval); | |
| }; | |
| if (!completed) await api.post({ url: `/quests/${quest.id}/video-progress`, body: { timestamp: secondsNeeded } }); | |
| console.log("Quest completed!"); | |
| r("Success"); | |
| } 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!"); | |
| r("Unavailable on browser"); | |
| } else { | |
| const res = await api.get({ url: `/applications/public?application_ids=${applicationId}` }); | |
| 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!"); | |
| r("Success"); | |
| 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 discord desktop app to complete the", questName, "quest!"); | |
| r("Unavailable on browser"); | |
| } 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!"); | |
| r("Success"); | |
| 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 voice chat for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.\nBe aware that you need another person to be in the voice chat in order to progress!`); | |
| }; | |
| } 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`; | |
| 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 sleep(20); | |
| if (progress >= secondsNeeded) { | |
| await api.post({ url: `/quests/${quest.id}/heartbeat`, body: { stream_key: streamKey, terminal: true } }); | |
| break; | |
| }; | |
| }; | |
| console.log("Quest completed!"); | |
| r("Success"); | |
| }; | |
| }); | |
| let quests = [...QuestsStore.quests.values()].filter(x => x.id !== "1412491570820812933" && x.userStatus?.enrolledAt && !x.userStatus?.completedAt && new Date(x.config.expiresAt).getTime() > Date.now()); | |
| if (quests.length === 0) { | |
| console.log("You don't have any uncompleted quests!"); | |
| } else { | |
| (async () => { | |
| console.log(`${quests.length} uncompleted quest${quests.length !== 1 ? "s" : ""} detected!`); | |
| const results = {}; | |
| for (let i = 0; i < quests.length; i++) { | |
| const quest = quests[i]; | |
| console.log(`Starting quest #${i + 1}...`) | |
| const result = await completeQuest(quest); | |
| if (!results[result]) results[result] = 0; | |
| results[result] += 1; | |
| console.log(`Finished quest #${i + 1}! (${i + 1}/${quests.length} | ${Math.floor(((i + 1) / quests.length) * 1000) / 10}%)`); | |
| }; | |
| console.log(`Finished all ${quests.length} quests! Results:\n${Object.keys(results).map(key => `${key}: ${results[key]}`).join('\n')}`); | |
| })(); | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment