Equicord/src/equicordplugins/questify/native.ts
2026-05-29 18:40:01 -04:00

123 lines
3.7 KiB
TypeScript

/*
* Vencord, a Discord client mod
* Copyright (c) 2026 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { BrowserWindow, type IpcMainInvokeEvent } from "electron";
export function canOpenDevTools(event: IpcMainInvokeEvent): boolean {
return !event.sender.isDestroyed();
}
export function openDevTools(event: IpcMainInvokeEvent): boolean {
if (!canOpenDevTools(event)) {
return false;
}
const window = BrowserWindow.fromWebContents(event.sender);
if (event.sender.isDevToolsOpened()) {
window?.focus();
return true;
}
event.sender.openDevTools();
return true;
}
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: "AUTH: " + JSON.stringify(authorization.error) };
}
const progressResult = await progress(appId, authorization.token, questTarget, questId, activityReferrer);
if (progressResult.error || !progressResult.success) {
return { success: false, error: "PROGRESS: " + JSON.stringify(progressResult.error) };
}
return { success: true, error: null };
}
function getActivityHeaders(questId: string, authToken: string = "", activityReferrer?: string): Record<string, string> {
const headers: Record<string, string> = {
"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<unknown> {
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(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 as const;
});
return { token, error };
}
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: getActivityHeaders(questId, token, activityReferrer),
body: JSON.stringify({ progress: questTarget }),
method: "POST",
mode: "cors",
credentials: "include",
})
.then(res => res.ok)
.catch(e => {
error = e;
return false;
});
return { success, error };
}