From 24250407c511b0a622946fd3816de97285b344b2 Mon Sep 17 00:00:00 2001 From: Etorix <92535668+EtorixDev@users.noreply.github.com> Date: Fri, 29 May 2026 15:40:01 -0700 Subject: [PATCH] Achievement Quest Fixes. (#1134) --- src/equicordplugins/questify/native.ts | 62 +++++++++++++++---- .../questify/utils/completion.ts | 18 +++++- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/equicordplugins/questify/native.ts b/src/equicordplugins/questify/native.ts index 1a852d895..934954a3a 100644 --- a/src/equicordplugins/questify/native.ts +++ b/src/equicordplugins/questify/native.ts @@ -28,46 +28,86 @@ export function openDevTools(event: IpcMainInvokeEvent): boolean { return true; } -export async function complete(_: IpcMainInvokeEvent, appId: string, authCode: string, questTarget: number): Promise<{ success: boolean; error: string | null; }> { - const authorization = await authorize(appId, authCode); +export async function complete(_: IpcMainInvokeEvent, appId: string, authCode: string, questTarget: number, questId: string, activityReferrer?: string): Promise<{ success: boolean; error: string | null; }> { + const authorization = await authorize(appId, authCode, questId, activityReferrer); if (authorization.error || !authorization.token) { - return { success: false, error: JSON.stringify(authorization.error) }; + return { success: false, error: "AUTH: " + JSON.stringify(authorization.error) }; } - const progressResult = await progress(appId, authorization.token, questTarget); + const progressResult = await progress(appId, authorization.token, questTarget, questId, activityReferrer); if (progressResult.error || !progressResult.success) { - return { success: false, error: JSON.stringify(progressResult.error) }; + return { success: false, error: "PROGRESS: " + JSON.stringify(progressResult.error) }; } return { success: true, error: null }; } -async function authorize(appId: string, authCode: string): Promise<{ token: string | false; error: unknown; }> { +function getActivityHeaders(questId: string, authToken: string = "", activityReferrer?: string): Record { + const headers: Record = { + "Content-Type": "application/json", + "X-Auth-Token": authToken, + "X-Discord-Quest-ID": questId, + }; + + if (activityReferrer) { + headers.Referer = activityReferrer; + } + + return headers; +} + +async function readJson(res: Response): Promise { + try { + return await res.json(); + } catch { + return null; + } +} + +function getResponseToken(data: unknown): string | false { + if (data != null && typeof data === "object" && "token" in data && typeof data.token === "string") { + return data.token; + } + + return false; +} + +async function authorize(appId: string, authCode: string, questId: string, activityReferrer?: string): Promise<{ token: string | false; error: unknown; }> { let error: unknown = null; const token = await fetch(`https://${appId}.discordsays.com/.proxy/acf/authorize`, { + headers: getActivityHeaders(questId, "", activityReferrer), body: JSON.stringify({ code: authCode }), method: "POST", mode: "cors", credentials: "include", }) - .then(res => res.json()) - .then(data => data.token) + .then(async res => { + const data = await readJson(res); + const token = getResponseToken(data); + + if (!res.ok || !token) { + error = { status: res.status, body: data }; + return false as const; + } + + return token; + }) .catch(e => { error = e; - return false; + return false as const; }); return { token, error }; } -async function progress(appId: string, token: string, questTarget: number): Promise<{ success: boolean; error: unknown; }> { +async function progress(appId: string, token: string, questTarget: number, questId: string, activityReferrer?: string): Promise<{ success: boolean; error: unknown; }> { let error: unknown = null; const success = await fetch(`https://${appId}.discordsays.com/.proxy/acf/quest/progress`, { - headers: { "x-auth-token": token }, + headers: getActivityHeaders(questId, token, activityReferrer), body: JSON.stringify({ progress: questTarget }), method: "POST", mode: "cors", diff --git a/src/equicordplugins/questify/utils/completion.ts b/src/equicordplugins/questify/utils/completion.ts index eeeaddc76..22339026b 100644 --- a/src/equicordplugins/questify/utils/completion.ts +++ b/src/equicordplugins/questify/utils/completion.ts @@ -89,6 +89,7 @@ const sendHeartbeat = findByCodeLazy(".QUESTS_HEARTBEAT(") as (options: { terminal?: boolean; executableFingerprint?: unknown; }) => Promise; +const getApplicationProxyTicket = findByCodeLazy("APPLICATION_PROXY_TICKET", "body.ticket") as (applicationId: string, channelId?: string) => Promise; export const enrollInQuest = findByCodeLazy('type:"QUESTS_ENROLL_BEGIN",') as (questId: string, options: QuestEnrollmentMetadata) => Promise; const getQuestOrbQuantity = findByCodeLazy("premiumOrbQuantity??", "orbQuantity") as ( config: Quest["config"], @@ -100,6 +101,21 @@ function resolveQuestCTA(taskType?: QuestTaskType): string | undefined { return !taskType ? undefined : [QuestTaskType.ACHIEVEMENT_IN_ACTIVITY, QuestTaskType.PLAY_ACTIVITY, QuestTaskType.WATCH_VIDEO].includes(taskType) ? QuestCTA.START_QUEST : QuestCTA.ACCEPT_QUEST; } +async function getActivityReferrer(appId: string): Promise { + try { + const proxyTicket = await getApplicationProxyTicket(appId); + const referrer = new URL(`https://${appId}.discordsays.com/`); + + referrer.searchParams.set("instance_id", "example-cl-instance"); + referrer.searchParams.set("platform", "desktop"); + referrer.searchParams.set("discord_proxy_ticket", proxyTicket); + + return referrer.toString(); + } catch (error) { + QL.error("AUTO_COMPLETE_ACHIEVEMENT_PROXY_TICKET_FAILED", { appId, error }); + } +} + export function makeEnrollmentData(args: QuestButtonAnalyticsArgs): QuestEnrollmentMetadata { return { questContent: args.analyticsCtxQuestContent, @@ -913,7 +929,7 @@ async function runAchievementQuest(quest: Quest, entry: AutoCompleteEntry, targe return false; } - const result = await QuestifyNative.complete(appId, authCode, target.adjusted); + const result = await QuestifyNative.complete(appId, authCode, target.adjusted, quest.id, await getActivityReferrer(appId)); const success = result.success === true; setQuestAutoCompleteProgress(quest, success ? target.adjusted : 0);