Last active 2 months ago

csmith1865's Avatar csmith1865 revised this gist 2 months ago. Go to revision

1 file changed, 91 insertions, 80 deletions

CompleteDiscordQuest.md

@@ -16,19 +16,19 @@ How to use this script:
16 16
17 17 ```js
18 18 delete window.$;
19 - let wpRequire;
20 - window.webpackChunkdiscord_app.push([[ Math.random() ], {}, (req) => { wpRequire = req; }]);
19 + let wpRequire = webpackChunkdiscord_app.push([[Symbol()], {}, r => r]);
20 + webpackChunkdiscord_app.pop();
21 21
22 - let ApplicationStreamingStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.getStreamerActiveStreamMetadata).exports.Z;
22 + let ApplicationStreamingStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.getStreamerActiveStreamMetadata).exports.Z;
23 23 let RunningGameStore = Object.values(wpRequire.c).find(x => x?.exports?.ZP?.getRunningGames).exports.ZP;
24 - let QuestsStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.getQuest).exports.Z;
25 - let ChannelStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.getAllThreadsForParent).exports.Z;
24 + let QuestsStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.getQuest).exports.Z;
25 + let ChannelStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.getAllThreadsForParent).exports.Z;
26 26 let GuildChannelStore = Object.values(wpRequire.c).find(x => x?.exports?.ZP?.getSFWDefaultChannel).exports.ZP;
27 - let FluxDispatcher = Object.values(wpRequire.c).find(x => x?.exports?.Z?.flushWaitQueue).exports.Z;
27 + let FluxDispatcher = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.flushWaitQueue).exports.Z;
28 28 let api = Object.values(wpRequire.c).find(x => x?.exports?.tn?.get).exports.tn;
29 29
30 30 let quest = [...QuestsStore.quests.values()].find(x => x.id !== "1248385850622869556" && x.userStatus?.enrolledAt && !x.userStatus?.completedAt && new Date(x.config.expiresAt).getTime() > Date.now())
31 - let isApp = navigator.userAgent.includes("Electron/")
31 + let isApp = typeof DiscordNative !== "undefined"
32 32 if(!quest) {
33 33 console.log("You don't have any uncompleted quests!")
34 34 } else {
@@ -36,108 +36,119 @@ if(!quest) {
36 36
37 37 const applicationId = quest.config.application.id
38 38 const applicationName = quest.config.application.name
39 - const taskName = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY"].find(x => quest.config.taskConfig.tasks[x] != null)
40 - const secondsNeeded = quest.config.taskConfig.tasks[taskName].target
41 - const secondsDone = quest.userStatus?.progress?.[taskName]?.value ?? 0
42 -
43 - if(taskName === "WATCH_VIDEO") {
44 - const tolerance = 2, speed = 10
45 - const diff = Math.floor((Date.now() - new Date(quest.userStatus.enrolledAt).getTime())/1000)
46 - const startingPoint = Math.min(Math.max(Math.ceil(secondsDone), diff), secondsNeeded)
47 - let fn = async () => {
48 - for(let i=startingPoint;i<=secondsNeeded;i+=speed) {
49 - try {
50 - await api.post({url: `/quests/${quest.id}/video-progress`, body: {timestamp: Math.min(secondsNeeded, i + Math.random())}})
51 - } catch(ex) {
52 - console.log("Failed to send increment of", i, ex.message)
39 + const questName = quest.config.messages.questName
40 + const taskConfig = quest.config.taskConfig ?? quest.config.taskConfigV2
41 + const taskName = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY", "WATCH_VIDEO_ON_MOBILE"].find(x => taskConfig.tasks[x] != null)
42 + const secondsNeeded = taskConfig.tasks[taskName].target
43 + let secondsDone = quest.userStatus?.progress?.[taskName]?.value ?? 0
44 +
45 + if(taskName === "WATCH_VIDEO" || taskName === "WATCH_VIDEO_ON_MOBILE") {
46 + const maxFuture = 10, speed = 7, interval = 1
47 + const enrolledAt = new Date(quest.userStatus.enrolledAt).getTime()
48 + let completed = false
49 + let fn = async () => {
50 + while(true) {
51 + const maxAllowed = Math.floor((Date.now() - enrolledAt)/1000) + maxFuture
52 + const diff = maxAllowed - secondsDone
53 + const timestamp = secondsDone + speed
54 + if(diff >= speed) {
55 + const res = await api.post({url: `/quests/${quest.id}/video-progress`, body: {timestamp: Math.min(secondsNeeded, timestamp + Math.random())}})
56 + completed = res.body.completed_at != null
57 + secondsDone = Math.min(secondsNeeded, timestamp)
58 + }
59 +
60 + if(timestamp >= secondsNeeded) {
61 + break
53 62 }
54 - await new Promise(resolve => setTimeout(resolve, tolerance * 1000))
63 + await new Promise(resolve => setTimeout(resolve, interval * 1000))
55 64 }
56 - if((secondsNeeded-secondsDone)%speed !== 0) {
65 + if(!completed) {
57 66 await api.post({url: `/quests/${quest.id}/video-progress`, body: {timestamp: secondsNeeded}})
58 67 }
59 68 console.log("Quest completed!")
60 69 }
61 70 fn()
62 - console.log(`Spoofing video for ${applicationName}. Wait for ${Math.ceil((secondsNeeded - startingPoint)/speed*tolerance)} more seconds.`)
71 + console.log(`Spoofing video for ${questName}.`)
63 72 } else if(taskName === "PLAY_ON_DESKTOP") {
64 73 if(!isApp) {
65 - console.log("This no longer works in browser for non-video quests. Use the desktop app to complete the", applicationName, "quest!")
74 + console.log("This no longer works in browser for non-video quests. Use the discord desktop app to complete the", questName, "quest!")
75 + } else {
76 + api.get({url: `/applications/public?application_ids=${applicationId}`}).then(res => {
77 + const appData = res.body[0]
78 + const exeName = appData.executables.find(x => x.os === "win32").name.replace(">","")
79 +
80 + const fakeGame = {
81 + cmdLine: `C:\\Program Files\\${appData.name}\\${exeName}`,
82 + exeName,
83 + exePath: `c:/program files/${appData.name.toLowerCase()}/${exeName}`,
84 + hidden: false,
85 + isLauncher: false,
86 + id: applicationId,
87 + name: appData.name,
88 + pid: pid,
89 + pidPath: [pid],
90 + processName: appData.name,
91 + start: Date.now(),
92 + }
93 + const realGames = RunningGameStore.getRunningGames()
94 + const fakeGames = [fakeGame]
95 + const realGetRunningGames = RunningGameStore.getRunningGames
96 + const realGetGameForPID = RunningGameStore.getGameForPID
97 + RunningGameStore.getRunningGames = () => fakeGames
98 + RunningGameStore.getGameForPID = (pid) => fakeGames.find(x => x.pid === pid)
99 + FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: realGames, added: [fakeGame], games: fakeGames})
100 +
101 + let fn = data => {
102 + let progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value)
103 + console.log(`Quest progress: ${progress}/${secondsNeeded}`)
104 +
105 + if(progress >= secondsNeeded) {
106 + console.log("Quest completed!")
107 +
108 + RunningGameStore.getRunningGames = realGetRunningGames
109 + RunningGameStore.getGameForPID = realGetGameForPID
110 + FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: [fakeGame], added: [], games: []})
111 + FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
112 + }
113 + }
114 + FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
115 +
116 + console.log(`Spoofed your game to ${applicationName}. Wait for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`)
117 + })
66 118 }
67 -
68 - api.get({url: `/applications/public?application_ids=${applicationId}`}).then(res => {
69 - const appData = res.body[0]
70 - const exeName = appData.executables.find(x => x.os === "win32").name.replace(">","")
71 -
72 - const games = RunningGameStore.getRunningGames()
73 - const fakeGame = {
74 - cmdLine: `C:\\Program Files\\${appData.name}\\${exeName}`,
75 - exeName,
76 - exePath: `c:/program files/${appData.name.toLowerCase()}/${exeName}`,
77 - hidden: false,
78 - isLauncher: false,
119 + } else if(taskName === "STREAM_ON_DESKTOP") {
120 + if(!isApp) {
121 + console.log("This no longer works in browser for non-video quests. Use the discord desktop app to complete the", questName, "quest!")
122 + } else {
123 + let realFunc = ApplicationStreamingStore.getStreamerActiveStreamMetadata
124 + ApplicationStreamingStore.getStreamerActiveStreamMetadata = () => ({
79 125 id: applicationId,
80 - name: appData.name,
81 - pid: pid,
82 - pidPath: [pid],
83 - processName: appData.name,
84 - start: Date.now(),
85 - }
86 - games.push(fakeGame)
87 - FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: [], added: [fakeGame], games: games})
126 + pid,
127 + sourceName: null
128 + })
88 129
89 130 let fn = data => {
90 - let progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value)
131 + let progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.STREAM_ON_DESKTOP.value)
91 132 console.log(`Quest progress: ${progress}/${secondsNeeded}`)
92 133
93 134 if(progress >= secondsNeeded) {
94 135 console.log("Quest completed!")
95 136
96 - const idx = games.indexOf(fakeGame)
97 - if(idx > -1) {
98 - games.splice(idx, 1)
99 - FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: [fakeGame], added: [], games: []})
100 - }
137 + ApplicationStreamingStore.getStreamerActiveStreamMetadata = realFunc
101 138 FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
102 139 }
103 140 }
104 141 FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
105 142
106 - console.log(`Spoofed your game to ${applicationName}. Wait for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`)
107 - })
108 - } else if(taskName === "STREAM_ON_DESKTOP") {
109 - if(!isApp) {
110 - console.log("This no longer works in browser for non-video quests. Use the desktop app to complete the", applicationName, "quest!")
111 - }
112 -
113 - let realFunc = ApplicationStreamingStore.getStreamerActiveStreamMetadata
114 - ApplicationStreamingStore.getStreamerActiveStreamMetadata = () => ({
115 - id: applicationId,
116 - pid,
117 - sourceName: null
118 - })
119 -
120 - let fn = data => {
121 - let progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.STREAM_ON_DESKTOP.value)
122 - console.log(`Quest progress: ${progress}/${secondsNeeded}`)
123 -
124 - if(progress >= secondsNeeded) {
125 - console.log("Quest completed!")
126 -
127 - ApplicationStreamingStore.getStreamerActiveStreamMetadata = realFunc
128 - FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
129 - }
143 + console.log(`Spoofed your stream to ${applicationName}. Stream any window in vc for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`)
144 + console.log("Remember that you need at least 1 other person to be in the vc!")
130 145 }
131 - FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
132 -
133 - console.log(`Spoofed your stream to ${applicationName}. Stream any window in vc for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`)
134 - console.log("Remember that you need at least 1 other person to be in the vc!")
135 146 } else if(taskName === "PLAY_ACTIVITY") {
136 147 const channelId = ChannelStore.getSortedPrivateChannels()[0]?.id ?? Object.values(GuildChannelStore.getAllGuilds()).find(x => x != null && x.VOCAL.length > 0).VOCAL[0].channel.id
137 148 const streamKey = `call:${channelId}:1`
138 149
139 150 let fn = async () => {
140 - console.log("Completing quest", applicationName, "-", quest.config.messages.questName)
151 + console.log("Completing quest", questName, "-", quest.config.messages.questName)
141 152
142 153 while(true) {
143 154 const res = await api.post({url: `/quests/${quest.id}/heartbeat`, body: {stream_key: streamKey, terminal: false}})

csmith1865's Avatar csmith1865 revised this gist 10 months ago. Go to revision

1 file changed, 206 insertions

CompleteDiscordQuest.md(file created)

@@ -0,0 +1,206 @@
1 + ## Complete Recent Discord Quest
2 +
3 + > [!NOTE]
4 + > This does not works in browser for non-video, non-activity quests! For stream/play quests use the desktop app!
5 +
6 + > [!NOTE]
7 + > When doing stream quests, you need at least 1 other account in the vc!
8 +
9 + How to use this script:
10 + 1. Accept a quest under User Settings -> Gift Inventory
11 + 2. Press <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>I</kbd> to open DevTools
12 + 3. Go to the `Console` tab
13 + 4. Paste the following code and hit enter:
14 + <details>
15 + <summary>Click to expand</summary>
16 +
17 + ```js
18 + delete window.$;
19 + let wpRequire;
20 + window.webpackChunkdiscord_app.push([[ Math.random() ], {}, (req) => { wpRequire = req; }]);
21 +
22 + let ApplicationStreamingStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.getStreamerActiveStreamMetadata).exports.Z;
23 + let RunningGameStore = Object.values(wpRequire.c).find(x => x?.exports?.ZP?.getRunningGames).exports.ZP;
24 + let QuestsStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.getQuest).exports.Z;
25 + let ChannelStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.getAllThreadsForParent).exports.Z;
26 + let GuildChannelStore = Object.values(wpRequire.c).find(x => x?.exports?.ZP?.getSFWDefaultChannel).exports.ZP;
27 + let FluxDispatcher = Object.values(wpRequire.c).find(x => x?.exports?.Z?.flushWaitQueue).exports.Z;
28 + let api = Object.values(wpRequire.c).find(x => x?.exports?.tn?.get).exports.tn;
29 +
30 + let quest = [...QuestsStore.quests.values()].find(x => x.id !== "1248385850622869556" && x.userStatus?.enrolledAt && !x.userStatus?.completedAt && new Date(x.config.expiresAt).getTime() > Date.now())
31 + let isApp = navigator.userAgent.includes("Electron/")
32 + if(!quest) {
33 + console.log("You don't have any uncompleted quests!")
34 + } else {
35 + const pid = Math.floor(Math.random() * 30000) + 1000
36 +
37 + const applicationId = quest.config.application.id
38 + const applicationName = quest.config.application.name
39 + const taskName = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY"].find(x => quest.config.taskConfig.tasks[x] != null)
40 + const secondsNeeded = quest.config.taskConfig.tasks[taskName].target
41 + const secondsDone = quest.userStatus?.progress?.[taskName]?.value ?? 0
42 +
43 + if(taskName === "WATCH_VIDEO") {
44 + const tolerance = 2, speed = 10
45 + const diff = Math.floor((Date.now() - new Date(quest.userStatus.enrolledAt).getTime())/1000)
46 + const startingPoint = Math.min(Math.max(Math.ceil(secondsDone), diff), secondsNeeded)
47 + let fn = async () => {
48 + for(let i=startingPoint;i<=secondsNeeded;i+=speed) {
49 + try {
50 + await api.post({url: `/quests/${quest.id}/video-progress`, body: {timestamp: Math.min(secondsNeeded, i + Math.random())}})
51 + } catch(ex) {
52 + console.log("Failed to send increment of", i, ex.message)
53 + }
54 + await new Promise(resolve => setTimeout(resolve, tolerance * 1000))
55 + }
56 + if((secondsNeeded-secondsDone)%speed !== 0) {
57 + await api.post({url: `/quests/${quest.id}/video-progress`, body: {timestamp: secondsNeeded}})
58 + }
59 + console.log("Quest completed!")
60 + }
61 + fn()
62 + console.log(`Spoofing video for ${applicationName}. Wait for ${Math.ceil((secondsNeeded - startingPoint)/speed*tolerance)} more seconds.`)
63 + } else if(taskName === "PLAY_ON_DESKTOP") {
64 + if(!isApp) {
65 + console.log("This no longer works in browser for non-video quests. Use the desktop app to complete the", applicationName, "quest!")
66 + }
67 +
68 + api.get({url: `/applications/public?application_ids=${applicationId}`}).then(res => {
69 + const appData = res.body[0]
70 + const exeName = appData.executables.find(x => x.os === "win32").name.replace(">","")
71 +
72 + const games = RunningGameStore.getRunningGames()
73 + const fakeGame = {
74 + cmdLine: `C:\\Program Files\\${appData.name}\\${exeName}`,
75 + exeName,
76 + exePath: `c:/program files/${appData.name.toLowerCase()}/${exeName}`,
77 + hidden: false,
78 + isLauncher: false,
79 + id: applicationId,
80 + name: appData.name,
81 + pid: pid,
82 + pidPath: [pid],
83 + processName: appData.name,
84 + start: Date.now(),
85 + }
86 + games.push(fakeGame)
87 + FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: [], added: [fakeGame], games: games})
88 +
89 + let fn = data => {
90 + let progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value)
91 + console.log(`Quest progress: ${progress}/${secondsNeeded}`)
92 +
93 + if(progress >= secondsNeeded) {
94 + console.log("Quest completed!")
95 +
96 + const idx = games.indexOf(fakeGame)
97 + if(idx > -1) {
98 + games.splice(idx, 1)
99 + FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: [fakeGame], added: [], games: []})
100 + }
101 + FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
102 + }
103 + }
104 + FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
105 +
106 + console.log(`Spoofed your game to ${applicationName}. Wait for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`)
107 + })
108 + } else if(taskName === "STREAM_ON_DESKTOP") {
109 + if(!isApp) {
110 + console.log("This no longer works in browser for non-video quests. Use the desktop app to complete the", applicationName, "quest!")
111 + }
112 +
113 + let realFunc = ApplicationStreamingStore.getStreamerActiveStreamMetadata
114 + ApplicationStreamingStore.getStreamerActiveStreamMetadata = () => ({
115 + id: applicationId,
116 + pid,
117 + sourceName: null
118 + })
119 +
120 + let fn = data => {
121 + let progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.STREAM_ON_DESKTOP.value)
122 + console.log(`Quest progress: ${progress}/${secondsNeeded}`)
123 +
124 + if(progress >= secondsNeeded) {
125 + console.log("Quest completed!")
126 +
127 + ApplicationStreamingStore.getStreamerActiveStreamMetadata = realFunc
128 + FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
129 + }
130 + }
131 + FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
132 +
133 + console.log(`Spoofed your stream to ${applicationName}. Stream any window in vc for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`)
134 + console.log("Remember that you need at least 1 other person to be in the vc!")
135 + } else if(taskName === "PLAY_ACTIVITY") {
136 + const channelId = ChannelStore.getSortedPrivateChannels()[0]?.id ?? Object.values(GuildChannelStore.getAllGuilds()).find(x => x != null && x.VOCAL.length > 0).VOCAL[0].channel.id
137 + const streamKey = `call:${channelId}:1`
138 +
139 + let fn = async () => {
140 + console.log("Completing quest", applicationName, "-", quest.config.messages.questName)
141 +
142 + while(true) {
143 + const res = await api.post({url: `/quests/${quest.id}/heartbeat`, body: {stream_key: streamKey, terminal: false}})
144 + const progress = res.body.progress.PLAY_ACTIVITY.value
145 + console.log(`Quest progress: ${progress}/${secondsNeeded}`)
146 +
147 + await new Promise(resolve => setTimeout(resolve, 20 * 1000))
148 +
149 + if(progress >= secondsNeeded) {
150 + await api.post({url: `/quests/${quest.id}/heartbeat`, body: {stream_key: streamKey, terminal: true}})
151 + break
152 + }
153 + }
154 +
155 + console.log("Quest completed!")
156 + }
157 + fn()
158 + }
159 + }
160 + ```
161 + </details>
162 +
163 + 5. Follow the printed instructions depending on what type of quest you have
164 + - If your quest says to "play" the game, you can just wait and do nothing
165 + - If your quest says to "stream" the game, join a vc with a friend or alt and stream any window
166 + 7. Wait for 15 minutes
167 + 8. You can now claim the reward in User Settings -> Gift Inventory!
168 +
169 + 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.
170 +
171 + ## FAQ
172 +
173 + **Q: Ctrl + Shift + I doesn't work**
174 +
175 + A: Either download the [ptb client](https://discord.com/api/downloads/distributions/app/installers/latest?channel=ptb&platform=win&arch=x64), or use [this](https://www.reddit.com/r/discordapp/comments/sc61n3/comment/hu4fw5x/) to enable DevTools on stable
176 +
177 +
178 + **Q: I get an error saying "Unauthorized"**
179 +
180 + A: Discord has patched the script from working in browsers. Use the desktop app, or alternatively find some extension which lets you change your User-Agent and append the string `Electron/` anywhere in it.
181 +
182 + They have also started checking how many people are in the vc, so make sure you join it on at least 1 other account.
183 +
184 +
185 + **Q: I get a syntax error/unexpected token error**
186 +
187 + A: Make sure your browser isn't auto-translating this website before copying the script. Turn off any translator extensions and try again.
188 +
189 +
190 + **Q: I get the error `Cannot read properties of undefined (reading 'nativeModules')`**
191 +
192 + A: You're on a modded client which is a browser wrapper (i.e. Vesktop). Use an actual desktop app.
193 +
194 +
195 + **Q: I get a different error**
196 +
197 + A: Make sure you're copy/pasting the script correctly and that you've have done all the steps.
198 +
199 +
200 + **Q: Can you make the script auto accept the quest/reward?**
201 +
202 + A: No. Both of those actions may show a captcha, so automating them is not a good idea. Just do the two clicks yourself.
203 +
204 + ___
205 +
206 + <sup>Side note: Please don't post your "fixed" or "improved" versions of the script in the comments. It creates unnecessary confusion, often doesn't fix anything, and sometimes puts other people's accounts at risk. I might redact/delete such comments without notice. Thank you for understanding.</sup>
Newer Older