-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlegacy_quest_script.js
More file actions
289 lines (234 loc) · 12.6 KB
/
Copy pathlegacy_quest_script.js
File metadata and controls
289 lines (234 loc) · 12.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/**
* @name Discord Quest Helper
* @description Automates Discord quests sequentially.
* @author @DMR3-CODE
* @original_author aamiaa (https://gist.github.com/aamiaa/204cd9d42013ded9faf646fae7f89fbb)
* @license GPL-3.0
* @version 1.0.0
*/
(async function () {
console.log("%c[Quest Helper] Started", "color: #7289da; font-weight: bold; font-size: 14px;");
// helper to remove jQuery text if present (user original script had this)
if (window.$) delete window.$;
// --- Webpack Extraction ---
// This part extracts internal Discord modules. It's standard for this type of script.
let wpRequire;
try {
wpRequire = webpackChunkdiscord_app.push([[Symbol()], {}, r => r]);
webpackChunkdiscord_app.pop();
} catch (e) {
console.error("[Quest Helper] Failed to hook into Webpack. Are you on Discord?", e);
return;
}
const findingModules = () => {
// Different Discord versions might have different mappings, so we try a few specific searches
const modules = Object.values(wpRequire.c);
const ApplicationStreamingStore = modules.find(x => x?.exports?.Z?.__proto__?.getStreamerActiveStreamMetadata)?.exports?.Z
?? modules.find(x => x?.exports?.A?.__proto__?.getStreamerActiveStreamMetadata)?.exports?.A;
const RunningGameStore = modules.find(x => x?.exports?.Ay?.getRunningGames)?.exports?.Ay
?? modules.find(x => x?.exports?.ZP?.getRunningGames)?.exports?.ZP;
const QuestsStore = modules.find(x => x?.exports?.A?.__proto__?.getQuest)?.exports?.A
?? modules.find(x => x?.exports?.Z?.__proto__?.getQuest)?.exports?.Z;
const ChannelStore = modules.find(x => x?.exports?.A?.__proto__?.getAllThreadsForParent)?.exports?.A
?? modules.find(x => x?.exports?.Z?.__proto__?.getAllThreadsForParent)?.exports?.Z;
const GuildChannelStore = modules.find(x => x?.exports?.Ay?.getSFWDefaultChannel)?.exports?.Ay
?? modules.find(x => x?.exports?.ZP?.getSFWDefaultChannel)?.exports?.ZP;
const FluxDispatcher = modules.find(x => x?.exports?.h?.__proto__?.flushWaitQueue)?.exports?.h
?? modules.find(x => x?.exports?.Z?.__proto__?.flushWaitQueue)?.exports?.Z;
const api = modules.find(x => x?.exports?.Bo?.get)?.exports?.Bo
?? modules.find(x => x?.exports?.tn?.get)?.exports?.tn;
return { ApplicationStreamingStore, RunningGameStore, QuestsStore, ChannelStore, GuildChannelStore, FluxDispatcher, api };
};
const { ApplicationStreamingStore, RunningGameStore, QuestsStore, ChannelStore, GuildChannelStore, FluxDispatcher, api } = findingModules();
if (!QuestsStore || !api) {
console.error("[Quest Helper] Could not find required Discord stores. The script might be outdated.");
return;
}
// --- Constants ---
const supportedTasks = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY", "WATCH_VIDEO_ON_MOBILE"];
const isApp = typeof DiscordNative !== "undefined";
// --- Helpers ---
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function completeVideoQuest(quest, taskName, secondsNeeded, secondsDone) {
console.log(`%c[Quest Helper] Starting Video Quest: ${quest.config.messages.questName}`, "color: yellow");
const maxFuture = 10, speed = 7, interval = 1; // Optimized params
const enrolledAt = new Date(quest.userStatus.enrolledAt).getTime();
// This simulates watching a video
while (secondsDone < secondsNeeded) {
const maxAllowed = Math.floor((Date.now() - enrolledAt) / 1000) + maxFuture;
const diff = maxAllowed - secondsDone;
const timestamp = secondsDone + speed;
if (diff >= speed) {
try {
const res = await api.post({
url: `/quests/${quest.id}/video-progress`,
body: { timestamp: Math.min(secondsNeeded, timestamp + Math.random()) }
});
if (res.body.completed_at != null) {
console.log(`%c[Quest Helper] Video Quest Completed!`, "color: green");
return;
}
} catch (e) {
console.error("API Error", e);
}
secondsDone = Math.min(secondsNeeded, timestamp);
}
await sleep(interval * 1000);
}
// Final check
try {
await api.post({ url: `/quests/${quest.id}/video-progress`, body: { timestamp: secondsNeeded } });
console.log(`%c[Quest Helper] Video Quest Finished.`, "color: green");
} catch (e) { }
}
async function completePlayDesktopQuest(quest, applicationId, applicationName, secondsNeeded, initialProgress) {
console.log(`%c[Quest Helper] Starting Play Desktop Quest: ${applicationName}`, "color: yellow");
if (!isApp) {
console.warn("This task requires the Desktop App.");
return;
}
return new Promise(async (resolve) => {
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 pid = Math.floor(Math.random() * 30000) + 1000;
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(),
};
// Hook Stores
const realGetRunningGames = RunningGameStore.getRunningGames;
const realGetGameForPID = RunningGameStore.getGameForPID;
const realGames = RunningGameStore.getRunningGames();
const fakeGames = [fakeGame];
RunningGameStore.getRunningGames = () => fakeGames;
RunningGameStore.getGameForPID = (pid) => fakeGames.find(x => x.pid === pid);
FluxDispatcher.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: realGames, added: [fakeGame], games: fakeGames });
const cleanup = () => {
RunningGameStore.getRunningGames = realGetRunningGames;
RunningGameStore.getGameForPID = realGetGameForPID;
FluxDispatcher.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: [fakeGame], added: [], games: [] });
FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", onHeartbeat);
};
const onHeartbeat = (data) => {
let progress = quest.config.configVersion === 1
? data.userStatus.streamProgressSeconds
: Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value);
console.log(`[Quest Helper] Progress: ${progress}/${secondsNeeded}`);
if (progress >= secondsNeeded) {
console.log(`%c[Quest Helper] Desktop Quest Completed!`, "color: green");
cleanup();
resolve();
}
};
FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", onHeartbeat);
console.log("Spoofing game... waiting for heartbeat updates.");
});
}
async function completeStreamDesktopQuest(quest, applicationId, applicationName, secondsNeeded) {
console.log(`%c[Quest Helper] Starting Stream Quest: ${applicationName}`, "color: yellow");
if (!isApp) {
console.warn("This task requires the Desktop App.");
return;
}
return new Promise((resolve) => {
const pid = Math.floor(Math.random() * 30000) + 1000;
let realFunc = ApplicationStreamingStore.getStreamerActiveStreamMetadata;
ApplicationStreamingStore.getStreamerActiveStreamMetadata = () => ({
id: applicationId,
pid,
sourceName: null
});
const cleanup = () => {
ApplicationStreamingStore.getStreamerActiveStreamMetadata = realFunc;
FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", onHeartbeat);
};
const onHeartbeat = (data) => {
let progress = quest.config.configVersion === 1
? data.userStatus.streamProgressSeconds
: Math.floor(data.userStatus.progress.STREAM_ON_DESKTOP.value);
console.log(`[Quest Helper] Progress: ${progress}/${secondsNeeded}`);
if (progress >= secondsNeeded) {
console.log(`%c[Quest Helper] Stream Quest Completed!`, "color: green");
cleanup();
resolve();
}
};
FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", onHeartbeat);
console.log("Spoofing stream... Make sure you are in a VC with at least 1 other person!");
});
}
async function completeActivityQuest(quest, secondsNeeded) {
console.log(`%c[Quest Helper] Starting Activity Quest`, "color: yellow");
// Try getting a private channel or a guild vocal channel
const channelId = ChannelStore.getSortedPrivateChannels()[0]?.id
?? Object.values(GuildChannelStore.getAllGuilds()).find(x => x && x.VOCAL.length > 0)?.VOCAL[0]?.channel.id;
if (!channelId) {
console.error("[Quest Helper] No available voice/private channel found to perform activity.");
return;
}
const streamKey = `call:${channelId}:1`;
while (true) {
try {
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 Helper] Progress: ${progress}/${secondsNeeded}`);
if (progress >= secondsNeeded) {
await api.post({ url: `/quests/${quest.id}/heartbeat`, body: { stream_key: streamKey, terminal: true } });
break;
}
} catch (e) {
console.error("Heartbeat error", e);
}
await sleep(20000); // 20s heartbeat
}
console.log(`%c[Quest Helper] Activity Quest Completed!`, "color: green");
}
// --- Main Loop ---
// Get eligible quests
const 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))
);
if (quests.length === 0) {
console.log("%c[Quest Helper] No eligible uncompleted quests found.", "color: orange");
return;
}
console.log(`%c[Quest Helper] Found ${quests.length} quest(s) to complete.`, "color: cyan");
// Sequential Execution
for (const quest of quests) {
const applicationId = quest.config.application.id;
const applicationName = quest.config.application.name;
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;
try {
if (taskName === "WATCH_VIDEO" || taskName === "WATCH_VIDEO_ON_MOBILE") {
await completeVideoQuest(quest, taskName, secondsNeeded, secondsDone);
} else if (taskName === "PLAY_ON_DESKTOP") {
await completePlayDesktopQuest(quest, applicationId, applicationName, secondsNeeded, secondsDone);
} else if (taskName === "STREAM_ON_DESKTOP") {
await completeStreamDesktopQuest(quest, applicationId, applicationName, secondsNeeded);
} else if (taskName === "PLAY_ACTIVITY") {
await completeActivityQuest(quest, secondsNeeded);
}
} catch (err) {
console.error(`[Quest Helper] Error doing quest ${quest.config.messages.questName}:`, err);
}
// Small buffer between quests
await sleep(2000);
}
console.log("%c[Quest Helper] All jobs finished.", "color: #7289da; font-weight: bold; font-size: 16px;");
})();