用程式碼破解 Discord 新推出的任務徽章

前言

Discord 最近新出了一個徽章,只要完成 Discord 出的任務就能拿到合作遊戲的遊戲內獎勵和展示在 Discord 個人資料上的任務徽章,但不是每個人都花 70~90 GB 的硬碟空間裝和 GTA5 差不多大小的牆國抄襲遊戲 ​原神,所以我們可以用一段程式碼來欺騙 Discord,讓系統以為你正在直播原神給朋友以完成任務。

教學

1. 到 Discord 設定 裡的 禮物庫存,可以看到多了個 任務 的部分,要求你直播 15 分鐘的原神給朋友看。



2. 按下 接受任務 後進任一伺服器的語音頻道,以防萬一可以請一位朋友或自己的小帳一起加入語音頻道,不然可能會失效。接著直播隨便一個視窗,任何視窗都行,重點是有開直播就好,我這邊是直播瀏覽器。



3. 都準備好後直接 Ctrl + Shift + i,可以看到右半邊跳出了開發者工具,其中的警告可以都忽略,不用太擔心。



4. 接著按到 Console 標籤,將下圖下方的程式碼貼上來後 Enter

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
let wpRequire;
window.webpackChunkdiscord_app.push([[ Math.random() ], {}, (req) => { wpRequire = req; }]);

let ApplicationStreamingStore, RunningGameStore, QuestsStore, ExperimentStore, FluxDispatcher, api
if(window.GLOBAL_ENV.SENTRY_TAGS.buildId === "366c746173a6ca0a801e9f4a4d7b6745e6de45d4") {
ApplicationStreamingStore = Object.values(wpRequire.c).find(x => x?.exports?.default?.getStreamerActiveStreamMetadata).exports.default;
RunningGameStore = Object.values(wpRequire.c).find(x => x?.exports?.default?.getRunningGames).exports.default;
QuestsStore = Object.values(wpRequire.c).find(x => x?.exports?.default?.getQuest).exports.default;
ExperimentStore = Object.values(wpRequire.c).find(x => x?.exports?.default?.getGuildExperiments).exports.default;
FluxDispatcher = Object.values(wpRequire.c).find(x => x?.exports?.default?.flushWaitQueue).exports.default;
api = Object.values(wpRequire.c).find(x => x?.exports?.getAPIBaseURL).exports.HTTP;
} else {
ApplicationStreamingStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.getStreamerActiveStreamMetadata).exports.Z;
RunningGameStore = Object.values(wpRequire.c).find(x => x?.exports?.ZP?.getRunningGames).exports.ZP;
QuestsStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.getQuest).exports.Z;
ExperimentStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.getGuildExperiments).exports.Z;
FluxDispatcher = Object.values(wpRequire.c).find(x => x?.exports?.Z?.flushWaitQueue).exports.Z;
api = Object.values(wpRequire.c).find(x => x?.exports?.tn?.get).exports.tn;
}

let quest = [...QuestsStore.quests.values()].find(x => x.userStatus?.enrolledAt && !x.userStatus?.completedAt && new Date(x.config.expiresAt).getTime() > Date.now())
let isApp = navigator.userAgent.includes("Electron/")
if(!isApp) {
console.log("This no longer works in browser. Use the desktop app!")
} else if(!quest) {
console.log("You don't have any uncompleted quests!")
} else {
const pid = Math.floor(Math.random() * 30000) + 1000

let applicationId, applicationName, secondsNeeded, secondsDone, canPlay
if(quest.config.configVersion === 1) {
applicationId = quest.config.applicationId
applicationName = quest.config.applicationName
secondsNeeded = quest.config.streamDurationRequirementMinutes * 60
secondsDone = quest.userStatus?.streamProgressSeconds ?? 0
canPlay = quest.config.variants.includes(2)
} else if(quest.config.configVersion === 2) {
applicationId = quest.config.application.id
applicationName = quest.config.application.name
canPlay = ExperimentStore.getUserExperimentBucket("2024-04_quest_playtime_task") > 0 && quest.config.taskConfig.tasks["PLAY_ON_DESKTOP"]
const taskName = canPlay ? "PLAY_ON_DESKTOP" : "STREAM_ON_DESKTOP"
secondsNeeded = quest.config.taskConfig.tasks[taskName].target
secondsDone = quest.userStatus?.progress?.[taskName]?.value ?? 0
}

if(canPlay) {
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 games = RunningGameStore.getRunningGames()
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(),
}
games.push(fakeGame)
FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: [], added: [fakeGame], games: games})

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!")

const idx = games.indexOf(fakeGame)
if(idx > -1) {
games.splice(idx, 1)
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 {
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 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!")
}
}



5. 可以在控制台看到 Quest progress: XX/XXX,代表任務進度正在跑了,也可以回到 禮物庫存 確認下進度有沒有加,接著直播別關掛著 15 分鐘即可。

原圖檔不知為何毀損,故以 2024/06/22 的 Discord 任務截圖來更新取代



15 分鐘後回到 Discord,可以看到任務完成了,獎勵也成功解鎖了,而 Discord 的個人檔案上也多了個任務徽章。

原圖檔不知為何毀損,故以 2024/06/22 的 Discord 任務截圖來更新取代



補充

如果在第 4 步 Enter 後跳出了下圖的警告,輸入 allow pasting 即可繼續。