/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import { showNotification } from "@api/Notifications";
import { Devs, EquicordDevs } from "@utils/constants";
import { getTheme, Theme } from "@utils/discord";
import definePlugin from "@utils/types";
import { findByCode, findByProps } from "@webpack";
import { FluxDispatcher, Forms, RestAPI, Text, UserStore } from "@webpack/common";
export default definePlugin({
name: "QuestCompleter",
description: "A plugin to complete quests without having the game installed.",
authors: [Devs.HappyEnderman, EquicordDevs.SerStars, EquicordDevs.thororen],
patches: [
{
find: "\"invite-button\"",
replacement: {
match: /(function .+?\(.+?\){let{inPopout:.+allowIdle.+?}=.+?\.\i\)\("popup"\),(.+?)=\[\];if\(.+?\){.+"chat-spacer"\)\)\),\(\d,.+?\.jsx\)\(.+?,{children:).+?}}/,
replace: "$1[$self.renderQuestButton(),...$2]})}}"
}
}
],
settingsAboutComponent() {
const isDesktop = navigator.userAgent.includes("discord/");
return (<>
{
isDesktop ?
The plugin should work properly because you are on the Desktop Client.
:
This plugin won't work because you are not on the Desktop Client.
}
>);
},
start() {
const currentUserId: string = UserStore.getCurrentUser().id;
window.currentUserId = currentUserId; // this is here because discord will lag if we get the current user id every time
},
renderQuestButton() {
const ToolTipButton = findByCode("}),color:\"currentColor\"})})}}");
const QuestsIcon = () => props => (
);
return (
<>
>
);
},
async openCompleteQuestUI() {
// check if user is sharing screen and there is someone that is watching the stream
const ApplicationStreamingStore = findByProps("getStreamerActiveStreamMetadata");
const RunningGameStore = findByProps("getRunningGames");
const ExperimentStore = findByProps("getGuildExperiments");
const QuestsStore = findByProps("getQuest");
const quest = [...QuestsStore.quests.values()].find(quest => quest.userStatus?.enrolledAt && !quest?.userStatus?.completedAt && new Date(quest?.config?.expiresAt) >= new Date());
const isApp = navigator.userAgent.includes("Electron/");
if (!isApp) {
showNotification({
title: "Quests Completer",
body: "This no longer works in browser. Use the desktop app!",
});
} else if (!quest) {
showNotification({
title: "Quests Completer",
body: "No Quests To Complete",
});
} else {
const pid = Math.floor(Math.random() * 30000) + 1000;
const theme = getTheme() === Theme.Light
? "light"
: "dark";
let applicationId, applicationName, secondsNeeded, secondsDone, canPlay, icon, questId;
if (quest.config.configVersion === 1) {
questId = quest.id;
applicationId = quest.config.applicationId;
applicationName = quest.config.applicationName;
secondsNeeded = quest.config.streamDurationRequirementMinutes * 60;
secondsDone = quest.userStatus?.streamProgressSeconds ?? 0;
icon = `https://cdn.discordapp.com/assets/quests/${questId}/${theme}/${quest.config.assets.gameTile}`;
canPlay = quest.config.variants.includes(2);
} else if (quest.config.configVersion === 2) {
questId = quest.id;
applicationId = quest.config.application.id;
applicationName = quest.config.application.name;
icon = `https://cdn.discordapp.com/assets/quests/${questId}/${theme}/${quest.config.assets.gameTile}`;
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) {
await RestAPI.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 });
const fn = data => {
const progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value);
showNotification({
title: `${applicationName} - Quests Completer`,
body: `Current progress: ${progress}/${secondsNeeded} minutes.`,
icon: icon,
});
if (progress >= secondsNeeded) {
showNotification({
title: `${applicationName} - Quests Completer`,
body: "Quest Completed",
icon: icon,
});
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);
});
} else {
const realFunc = ApplicationStreamingStore.getStreamerActiveStreamMetadata;
ApplicationStreamingStore.getStreamerActiveStreamMetadata = () => ({
id: applicationId,
pid,
sourceName: null
});
const fn = data => {
const progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.STREAM_ON_DESKTOP.value);
showNotification({
title: `${applicationName} - Quests Completer`,
body: `Current progress: ${progress}/${secondsNeeded} minutes.`,
icon: icon,
});
if (progress >= secondsNeeded) {
showNotification({
title: `${applicationName} - Quests Completer`,
body: "Quest Completed",
icon: icon,
});
ApplicationStreamingStore.getStreamerActiveStreamMetadata = realFunc;
FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn);
}
};
FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn);
}
return;
}
}
});