mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-09 14:43:03 -04:00
Merge Dev
This commit is contained in:
commit
a62029833f
10 changed files with 204 additions and 127 deletions
|
@ -110,6 +110,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
|
||||||
- PolishWording (Grammar) by Samwich
|
- PolishWording (Grammar) by Samwich
|
||||||
- PlatformSpoofer by Drag
|
- PlatformSpoofer by Drag
|
||||||
- PurgeMessages by bhop & nyx
|
- PurgeMessages by bhop & nyx
|
||||||
|
- QuestCompleter by HappyEnderman, SerStars, maintained by thororen
|
||||||
- QuestionMarkReplacement by nyx
|
- QuestionMarkReplacement by nyx
|
||||||
- Quoter by Samwich
|
- Quoter by Samwich
|
||||||
- RemixMe by kvba
|
- RemixMe by kvba
|
||||||
|
@ -163,7 +164,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
|
||||||
|
|
||||||
### Discord Desktop Only
|
### Discord Desktop Only
|
||||||
- MediaDownloader by Colorman
|
- MediaDownloader by Colorman
|
||||||
- QuestCompleter by HappyEnderman, SerStars, maintained by thororen
|
|
||||||
- StatusWhilePlaying by thororen
|
- StatusWhilePlaying by thororen
|
||||||
|
|
||||||
### Equicord Devbuilds Only
|
### Equicord Devbuilds Only
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addChatBarButton, ChatBarButton } from "@api/ChatButtons";
|
import { addChatBarButton, ChatBarButton, removeChatBarButton } from "@api/ChatButtons";
|
||||||
import {
|
import {
|
||||||
ApplicationCommandInputType,
|
ApplicationCommandInputType,
|
||||||
ApplicationCommandOptionType,
|
ApplicationCommandOptionType,
|
||||||
|
@ -12,7 +12,6 @@ import {
|
||||||
} from "@api/Commands";
|
} from "@api/Commands";
|
||||||
import * as DataStore from "@api/DataStore";
|
import * as DataStore from "@api/DataStore";
|
||||||
import { addPreSendListener, removePreSendListener, SendListener } from "@api/MessageEvents";
|
import { addPreSendListener, removePreSendListener, SendListener } from "@api/MessageEvents";
|
||||||
import { removeButton } from "@api/MessagePopover";
|
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { sleep } from "@utils/misc";
|
import { sleep } from "@utils/misc";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
|
@ -273,7 +272,7 @@ export default definePlugin({
|
||||||
await DataStore.set("encryptcordGroupMembers", {});
|
await DataStore.set("encryptcordGroupMembers", {});
|
||||||
},
|
},
|
||||||
async stop() {
|
async stop() {
|
||||||
removeButton("Encryptcord");
|
removeChatBarButton("Encryptcord");
|
||||||
if (await DataStore.get("encryptcordGroup") === true) {
|
if (await DataStore.get("encryptcordGroup") === true) {
|
||||||
await leave("", { channel: { id: await DataStore.get("encryptcordChannelId") } });
|
await leave("", { channel: { id: await DataStore.get("encryptcordChannelId") } });
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addChatBarButton, ChatBarButton } from "@api/ChatButtons";
|
import { addChatBarButton, ChatBarButton, removeChatBarButton } from "@api/ChatButtons";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { getCurrentChannel, sendMessage } from "@utils/discord";
|
import { getCurrentChannel, sendMessage } from "@utils/discord";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
|
@ -27,7 +27,6 @@ export default definePlugin({
|
||||||
description: "Adds a chatbar button to meow in chat",
|
description: "Adds a chatbar button to meow in chat",
|
||||||
authors:
|
authors:
|
||||||
[Devs.Samwich],
|
[Devs.Samwich],
|
||||||
start() {
|
start: () => addChatBarButton("Meow", ChatBarIcon),
|
||||||
addChatBarButton("vc-meow", ChatBarIcon);
|
stop: () => removeChatBarButton("Meow")
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
.quest-warning {
|
|
||||||
font-size: 16px !important;
|
|
||||||
background-color: rgb(240 71 71 / 10%) !important;
|
|
||||||
color: rgb(240 71 71) !important;
|
|
||||||
border: 1px solid rgb(240 71 71 / 60%) !important;
|
|
||||||
border-radius: 5px !important;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 6px 10px;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 10px;
|
|
||||||
font-style: bold;
|
|
||||||
}
|
|
|
@ -16,8 +16,6 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "./style.css";
|
|
||||||
|
|
||||||
import { showNotification } from "@api/Notifications";
|
import { showNotification } from "@api/Notifications";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs, EquicordDevs } from "@utils/constants";
|
import { Devs, EquicordDevs } from "@utils/constants";
|
||||||
|
@ -25,7 +23,7 @@ import { getTheme, Theme } from "@utils/discord";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByProps, findExportedComponentLazy } from "@webpack";
|
import { findByProps, findExportedComponentLazy } from "@webpack";
|
||||||
import { Button, FluxDispatcher, Forms, RestAPI, Tooltip, UserStore } from "@webpack/common";
|
import { Button, FluxDispatcher, RestAPI, Tooltip, UserStore } from "@webpack/common";
|
||||||
const HeaderBarIcon = findExportedComponentLazy("Icon", "Divider");
|
const HeaderBarIcon = findExportedComponentLazy("Icon", "Divider");
|
||||||
const isApp = navigator.userAgent.includes("Electron/");
|
const isApp = navigator.userAgent.includes("Electron/");
|
||||||
|
|
||||||
|
@ -64,13 +62,12 @@ function ToolBarHeader() {
|
||||||
async function openCompleteQuestUI() {
|
async function openCompleteQuestUI() {
|
||||||
const ApplicationStreamingStore = findByProps("getStreamerActiveStreamMetadata");
|
const ApplicationStreamingStore = findByProps("getStreamerActiveStreamMetadata");
|
||||||
const RunningGameStore = findByProps("getRunningGames");
|
const RunningGameStore = findByProps("getRunningGames");
|
||||||
const ExperimentStore = findByProps("getGuildExperiments");
|
|
||||||
const QuestsStore = findByProps("getQuest");
|
const QuestsStore = findByProps("getQuest");
|
||||||
const quest = [...QuestsStore.quests.values()].find(x => x.id !== "1248385850622869556" && x.userStatus?.enrolledAt && !x.userStatus?.completedAt && new Date(x.config.expiresAt).getTime() > Date.now());
|
const quest = [...QuestsStore.quests.values()].find(x => x.id !== "1248385850622869556" && x.userStatus?.enrolledAt && !x.userStatus?.completedAt && new Date(x.config.expiresAt).getTime() > Date.now());
|
||||||
|
|
||||||
if (!quest) {
|
if (!quest) {
|
||||||
showNotification({
|
showNotification({
|
||||||
title: "Quests Completer",
|
title: "Quest Completer",
|
||||||
body: "No Quests To Complete",
|
body: "No Quests To Complete",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -79,27 +76,52 @@ async function openCompleteQuestUI() {
|
||||||
? "light"
|
? "light"
|
||||||
: "dark";
|
: "dark";
|
||||||
|
|
||||||
let applicationId, applicationName, secondsNeeded, secondsDone, canPlay, icon, questId;
|
const applicationId = quest.config.application.id;
|
||||||
if (quest.config.configVersion === 1) {
|
const applicationName = quest.config.application.name;
|
||||||
questId = quest.id;
|
const taskName = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP"].find(x => quest.config.taskConfig.tasks[x] != null);
|
||||||
applicationId = quest.config.applicationId;
|
// @ts-ignore
|
||||||
applicationName = quest.config.applicationName;
|
const secondsNeeded = quest.config.taskConfig.tasks[taskName].target;
|
||||||
secondsNeeded = quest.config.streamDurationRequirementMinutes * 60;
|
// @ts-ignore
|
||||||
secondsDone = quest.userStatus?.streamProgressSeconds ?? 0;
|
const secondsDone = quest.userStatus?.progress?.[taskName]?.value ?? 0;
|
||||||
icon = `https://cdn.discordapp.com/assets/quests/${questId}/${theme}/${quest.config.assets.gameTile}`;
|
const icon = `https://cdn.discordapp.com/assets/quests/${quest.id}/${theme}/${quest.config.assets.gameTile}`;
|
||||||
canPlay = quest.config.variants.includes(2);
|
if (taskName === "WATCH_VIDEO") {
|
||||||
} else if (quest.config.configVersion === 2) {
|
const tolerance = 2, speed = 10;
|
||||||
questId = quest.id;
|
const diff = Math.floor((Date.now() - new Date(quest.userStatus.enrolledAt).getTime()) / 1000);
|
||||||
applicationId = quest.config.application.id;
|
const startingPoint = Math.min(Math.max(Math.ceil(secondsDone), diff), secondsNeeded);
|
||||||
applicationName = quest.config.application.name;
|
const fn = async () => {
|
||||||
icon = `https://cdn.discordapp.com/assets/quests/${questId}/${theme}/${quest.config.assets.gameTile}`;
|
for (let i = startingPoint; i <= secondsNeeded; i += speed) {
|
||||||
canPlay = ExperimentStore.getUserExperimentBucket("2024-04_quest_playtime_task") > 0 && quest.config.taskConfig.tasks.PLAY_ON_DESKTOP;
|
try {
|
||||||
const taskName = canPlay ? "PLAY_ON_DESKTOP" : "STREAM_ON_DESKTOP";
|
await RestAPI.post({ url: `/quests/${quest.id}/video-progress`, body: { timestamp: Math.min(secondsNeeded, i + Math.random()) } });
|
||||||
secondsNeeded = quest.config.taskConfig.tasks[taskName]?.target;
|
} catch (ex) {
|
||||||
secondsDone = quest.userStatus?.progress?.[taskName]?.value ?? 0;
|
console.log("Failed to send increment of", i, ex);
|
||||||
}
|
}
|
||||||
if (canPlay) {
|
await new Promise(resolve => setTimeout(resolve, tolerance * 1000));
|
||||||
await RestAPI.get({ url: `/applications/public?application_ids=${applicationId}` }).then(res => {
|
}
|
||||||
|
if ((secondsNeeded - secondsDone) % speed !== 0) {
|
||||||
|
await RestAPI.post({ url: `/quests/${quest.id}/video-progress`, body: { timestamp: secondsNeeded } });
|
||||||
|
showNotification({
|
||||||
|
title: `${applicationName} - Quest Completer`,
|
||||||
|
body: "Quest Completed.",
|
||||||
|
icon: icon,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fn();
|
||||||
|
showNotification({
|
||||||
|
title: `${applicationName} - Quest Completer`,
|
||||||
|
body: `Wait for ${Math.ceil((secondsNeeded - startingPoint) / speed * tolerance)} more seconds.`,
|
||||||
|
icon: icon,
|
||||||
|
});
|
||||||
|
console.log(`Spoofing video for ${applicationName}.`);
|
||||||
|
} else if (taskName === "PLAY_ON_DESKTOP") {
|
||||||
|
if (!isApp) {
|
||||||
|
showNotification({
|
||||||
|
title: `${applicationName} - Quest Completer`,
|
||||||
|
body: `${applicationName}'s quest requires the desktop app.`,
|
||||||
|
icon: icon,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RestAPI.get({ url: `/applications/public?application_ids=${applicationId}` }).then(res => {
|
||||||
const appData = res.body[0];
|
const appData = res.body[0];
|
||||||
const exeName = appData.executables.find(x => x.os === "win32").name.replace(">", "");
|
const exeName = appData.executables.find(x => x.os === "win32").name.replace(">", "");
|
||||||
|
|
||||||
|
@ -123,14 +145,14 @@ async function openCompleteQuestUI() {
|
||||||
const fn = data => {
|
const fn = data => {
|
||||||
const progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value);
|
const progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value);
|
||||||
showNotification({
|
showNotification({
|
||||||
title: `${applicationName} - Quests Completer`,
|
title: `${applicationName} - Quest Completer`,
|
||||||
body: `Current progress: ${progress}/${secondsNeeded} seconds.`,
|
body: `Current progress: ${progress}/${secondsNeeded} seconds.`,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (progress >= secondsNeeded) {
|
if (progress >= secondsNeeded) {
|
||||||
showNotification({
|
showNotification({
|
||||||
title: `${applicationName} - Quests Completer`,
|
title: `${applicationName} - Quest Completer`,
|
||||||
body: "Quest Completed.",
|
body: "Quest Completed.",
|
||||||
icon: icon,
|
icon: icon,
|
||||||
});
|
});
|
||||||
|
@ -144,16 +166,29 @@ async function openCompleteQuestUI() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn);
|
FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn);
|
||||||
|
console.log(`Spoofed your game to ${applicationName}.`);
|
||||||
});
|
});
|
||||||
} else {
|
} else if (taskName === "STREAM_ON_DESKTOP") {
|
||||||
const stream = ApplicationStreamingStore.getAnyStreamForUser(UserStore.getCurrentUser()?.id);
|
if (!isApp) {
|
||||||
if (!stream) {
|
|
||||||
showNotification({
|
showNotification({
|
||||||
title: "You're not streaming - Quests Completer",
|
title: `${applicationName} - Quest Completer`,
|
||||||
body: `${applicationName} requires you to be streaming.`,
|
body: `${applicationName}'s quest requires the desktop app.`,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const stream = ApplicationStreamingStore.getAnyStreamForUser(UserStore.getCurrentUser()?.id);
|
||||||
|
if (!stream) {
|
||||||
|
showNotification({
|
||||||
|
title: "You're not streaming - Quest Completer",
|
||||||
|
body: `${applicationName} requires you to be streaming.\nPlease stream any window in vc.`,
|
||||||
|
icon: icon,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
showNotification({
|
||||||
|
title: `${applicationName} - Quest Completer`,
|
||||||
|
body: "Remember that you need at least 1 other person to be in the vc!",
|
||||||
|
icon: icon,
|
||||||
|
});
|
||||||
const realFunc = ApplicationStreamingStore.getStreamerActiveStreamMetadata;
|
const realFunc = ApplicationStreamingStore.getStreamerActiveStreamMetadata;
|
||||||
ApplicationStreamingStore.getStreamerActiveStreamMetadata = () => ({
|
ApplicationStreamingStore.getStreamerActiveStreamMetadata = () => ({
|
||||||
id: applicationId,
|
id: applicationId,
|
||||||
|
@ -164,14 +199,14 @@ async function openCompleteQuestUI() {
|
||||||
const fn = data => {
|
const fn = data => {
|
||||||
const progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.STREAM_ON_DESKTOP.value);
|
const progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.STREAM_ON_DESKTOP.value);
|
||||||
showNotification({
|
showNotification({
|
||||||
title: `${applicationName} - Quests Completer`,
|
title: `${applicationName} - Quest Completer`,
|
||||||
body: `Current progress: ${progress}/${secondsNeeded} seconds.`,
|
body: `Current progress: ${progress}/${secondsNeeded} seconds.`,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (progress >= secondsNeeded) {
|
if (progress >= secondsNeeded) {
|
||||||
showNotification({
|
showNotification({
|
||||||
title: `${applicationName} - Quests Completer`,
|
title: `${applicationName} - Quest Completer`,
|
||||||
body: "Quest Completed.",
|
body: "Quest Completed.",
|
||||||
icon: icon,
|
icon: icon,
|
||||||
});
|
});
|
||||||
|
@ -181,6 +216,7 @@ async function openCompleteQuestUI() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn);
|
FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn);
|
||||||
|
console.log(`Spoofed your stream to ${applicationName}.`);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -190,11 +226,6 @@ export default definePlugin({
|
||||||
name: "QuestCompleter",
|
name: "QuestCompleter",
|
||||||
description: "A plugin to complete quests without having the game installed.",
|
description: "A plugin to complete quests without having the game installed.",
|
||||||
authors: [Devs.HappyEnderman, EquicordDevs.SerStars, EquicordDevs.thororen],
|
authors: [Devs.HappyEnderman, EquicordDevs.SerStars, EquicordDevs.thororen],
|
||||||
settingsAboutComponent: () => <>
|
|
||||||
<Forms.FormText className="remixme-warning">
|
|
||||||
We can't guarantee this plugin won't get you warned or banned.
|
|
||||||
</Forms.FormText>
|
|
||||||
</>,
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "\"invite-button\"",
|
find: "\"invite-button\"",
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addChatBarButton, ChatBarButton } from "@api/ChatButtons";
|
import { addChatBarButton, ChatBarButton, removeChatBarButton } from "@api/ChatButtons";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { getCurrentChannel, sendMessage } from "@utils/discord";
|
import { getCurrentChannel, sendMessage } from "@utils/discord";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
|
@ -26,7 +26,6 @@ export default definePlugin({
|
||||||
name: "Woof",
|
name: "Woof",
|
||||||
description: "Adds a chatbar button to woof in chat",
|
description: "Adds a chatbar button to woof in chat",
|
||||||
authors: [Devs.Samwich],
|
authors: [Devs.Samwich],
|
||||||
start() {
|
start: () => addChatBarButton("Woof", ChatBarIcon),
|
||||||
addChatBarButton("vc-woof", ChatBarIcon);
|
stop: () => removeChatBarButton("Woof")
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,11 +23,14 @@ export default definePlugin({
|
||||||
name: "MessagePopoverAPI",
|
name: "MessagePopoverAPI",
|
||||||
description: "API to add buttons to message popovers.",
|
description: "API to add buttons to message popovers.",
|
||||||
authors: [Devs.KingFish, Devs.Ven, Devs.Nuckyz],
|
authors: [Devs.KingFish, Devs.Ven, Devs.Nuckyz],
|
||||||
patches: [{
|
patches: [
|
||||||
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
|
{
|
||||||
replacement: {
|
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
|
||||||
match: /\.jsx\)\((\i\.\i),\{label:\i\.\i\.string\(\i\.\i#{intl::MESSAGE_ACTION_REPLY}.{0,200}?"reply-self".{0,50}?\}\):null(?=,.+?message:(\i))/,
|
replacement: {
|
||||||
replace: "$&,Vencord.Api.MessagePopover._buildPopoverElements($1,$2)"
|
match: /(?<=:null),(.{0,40}togglePopout:.+?}\))\]}\):null,(?<=\((\i\.\i),{label:.+?:null,(\i&&!\i)\?\(0,\i\.jsxs?\)\(\i\.Fragment.+?message:(\i).+?)/,
|
||||||
|
replace: (_, ReactButton, ButtonComponent, showReactButton, message) => "" +
|
||||||
|
`]}):null,Vencord.Api.MessagePopover._buildPopoverElements(${ButtonComponent},${message}),${showReactButton}?${ReactButton}:null,`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}],
|
]
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Settings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { runtimeHashMessageKey } from "@utils/intlHash";
|
import { runtimeHashMessageKey } from "@utils/intlHash";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
|
@ -27,6 +27,21 @@ import { Message } from "discord-types/general";
|
||||||
|
|
||||||
const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked");
|
const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked");
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
ignoreBlockedMessages: {
|
||||||
|
description: "Completely ignores (recent) incoming messages from blocked users (locally).",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false,
|
||||||
|
restartNeeded: true,
|
||||||
|
},
|
||||||
|
hideRepliesToBlockedMessages: {
|
||||||
|
description: "Hides replies to blocked messages.",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false,
|
||||||
|
restartNeeded: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
interface MessageDeleteProps {
|
interface MessageDeleteProps {
|
||||||
// Internal intl message for BLOCKED_MESSAGE_COUNT
|
// Internal intl message for BLOCKED_MESSAGE_COUNT
|
||||||
collapsedReason: () => any;
|
collapsedReason: () => any;
|
||||||
|
@ -35,7 +50,9 @@ interface MessageDeleteProps {
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "NoBlockedMessages",
|
name: "NoBlockedMessages",
|
||||||
description: "Hides all blocked messages from chat completely.",
|
description: "Hides all blocked messages from chat completely.",
|
||||||
authors: [Devs.rushii, Devs.Samu],
|
authors: [Devs.rushii, Devs.Samu, Devs.Elvyra],
|
||||||
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "#{intl::BLOCKED_MESSAGES_HIDE}",
|
find: "#{intl::BLOCKED_MESSAGES_HIDE}",
|
||||||
|
@ -51,49 +68,40 @@ export default definePlugin({
|
||||||
'"ReadStateStore"'
|
'"ReadStateStore"'
|
||||||
].map(find => ({
|
].map(find => ({
|
||||||
find,
|
find,
|
||||||
predicate: () => Settings.plugins.NoBlockedMessages.ignoreBlockedMessages === true,
|
predicate: () => settings.store.ignoreBlockedMessages === true,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(?<=MESSAGE_CREATE:function\((\i)\){)/,
|
match: /(?<=MESSAGE_CREATE:function\((\i)\){)/,
|
||||||
replace: (_, props) => `if($self.isBlocked(${props}.message))return;`
|
replace: (_, props) => `if($self.isBlocked(${props}.message)||$self.isReplyToBlocked(${props}.message))return;`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
find: ".messageListItem",
|
find: "referencedUsernameProfile,referencedAvatarProfile",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /(?<=\i=)(?=\(0,(\i)\.jsx)/,
|
{
|
||||||
replace: "!$self.isReplyToBlocked(arguments[0].message)&&"
|
match: /CUSTOM_GIFT.*?=(?=\(0,\i.jsx\)\(\i.FocusRing)/,
|
||||||
}
|
replace: "$&!$self.isReplyToBlocked(arguments[0].message)&&",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
options: {
|
|
||||||
ignoreBlockedMessages: {
|
|
||||||
description: "Completely ignores (recent) incoming messages from blocked users (locally).",
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
default: false,
|
|
||||||
restartNeeded: true,
|
|
||||||
},
|
},
|
||||||
hideRepliesToBlockedMessages: {
|
],
|
||||||
description: "Hide replies to messages made by users you've blocked",
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
default: false,
|
|
||||||
restartNeeded: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
isReplyToBlocked(message: Message) {
|
isReplyToBlocked(message: Message) {
|
||||||
if (!Settings.plugins.NoBlockedMessages.hideRepliesToBlockedMessages)
|
if (!settings.store.hideRepliesToBlockedMessages) return false;
|
||||||
return false;
|
try {
|
||||||
|
const { messageReference } = message;
|
||||||
|
if (!messageReference) return false;
|
||||||
|
|
||||||
const { messageReference } = message;
|
const replyMessage = MessageStore.getMessage(messageReference.channel_id, messageReference.message_id);
|
||||||
if (!messageReference) return false;
|
|
||||||
const replyMessage = MessageStore.getMessage(messageReference.channel_id, messageReference.message_id);
|
return replyMessage ? this.isBlocked(replyMessage) : false;
|
||||||
return this.isBlocked(replyMessage);
|
} catch (e) {
|
||||||
|
new Logger("NoBlockedMessages").error("Failed to check if referenced message is blocked:", e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
isBlocked(message: Message | undefined) {
|
isBlocked(message: Message) {
|
||||||
if (!message) return false;
|
|
||||||
try {
|
try {
|
||||||
return RelationshipStore.isBlocked(message.author.id);
|
return RelationshipStore.isBlocked(message.author.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -105,7 +113,7 @@ export default definePlugin({
|
||||||
try {
|
try {
|
||||||
return props.collapsedReason() === i18n.t[runtimeHashMessageKey("BLOCKED_MESSAGE_COUNT")]();
|
return props.collapsedReason() === i18n.t[runtimeHashMessageKey("BLOCKED_MESSAGE_COUNT")]();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
new Logger("NoBlockedMessages").error("Failed to hide blocked message:", e);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import "./style.css";
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs, EquicordDevs } from "@utils/constants";
|
||||||
import { canonicalizeMatch } from "@utils/patches";
|
import { canonicalizeMatch } from "@utils/patches";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
|
@ -33,24 +33,38 @@ const ChannelListClasses = findByPropsLazy("modeMuted", "modeSelected", "unread"
|
||||||
|
|
||||||
const enum ShowMode {
|
const enum ShowMode {
|
||||||
LockIcon,
|
LockIcon,
|
||||||
HiddenIconWithMutedStyle
|
LockIconRight,
|
||||||
|
EyeIconRight,
|
||||||
|
}
|
||||||
|
|
||||||
|
const enum ChannelStyle {
|
||||||
|
Classic,
|
||||||
|
Muted,
|
||||||
|
Unread,
|
||||||
|
MutedUnread,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONNECT = 1n << 20n;
|
const CONNECT = 1n << 20n;
|
||||||
|
|
||||||
export const settings = definePluginSettings({
|
export const settings = definePluginSettings({
|
||||||
hideUnreads: {
|
channelStyle: {
|
||||||
description: "Hide Unreads",
|
description: "The style used to display hidden channels.",
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.SELECT,
|
||||||
default: true,
|
options: [
|
||||||
|
{ label: "Classic", value: ChannelStyle.Classic, default: true },
|
||||||
|
{ label: "Muted", value: ChannelStyle.Muted },
|
||||||
|
{ label: "Show Unreads", value: ChannelStyle.Unread },
|
||||||
|
{ label: "Muted and Show Unreads", value: ChannelStyle.MutedUnread }
|
||||||
|
],
|
||||||
restartNeeded: true
|
restartNeeded: true
|
||||||
},
|
},
|
||||||
showMode: {
|
showMode: {
|
||||||
description: "The mode used to display hidden channels.",
|
description: "The mode used to display hidden channels.",
|
||||||
type: OptionType.SELECT,
|
type: OptionType.SELECT,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Plain style with Lock Icon instead", value: ShowMode.LockIcon, default: true },
|
{ label: "Lock Icon replacing channel icon", value: ShowMode.LockIcon, default: true },
|
||||||
{ label: "Muted style with hidden eye icon on the right", value: ShowMode.HiddenIconWithMutedStyle },
|
{ label: "Eye icon on the right", value: ShowMode.EyeIconRight },
|
||||||
|
{ label: "Lock icon on the right", value: ShowMode.LockIconRight }
|
||||||
],
|
],
|
||||||
restartNeeded: true
|
restartNeeded: true
|
||||||
},
|
},
|
||||||
|
@ -68,7 +82,7 @@ function isUncategorized(objChannel: { channel: Channel; comparator: number; })
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "ShowHiddenChannels",
|
name: "ShowHiddenChannels",
|
||||||
description: "Show channels that you do not have access to view.",
|
description: "Show channels that you do not have access to view.",
|
||||||
authors: [Devs.BigDuck, Devs.AverageReactEnjoyer, Devs.D3SOX, Devs.Ven, Devs.Nuckyz, Devs.Nickyux, Devs.dzshn],
|
authors: [Devs.BigDuck, Devs.AverageReactEnjoyer, Devs.D3SOX, Devs.Ven, Devs.Nuckyz, Devs.Nickyux, Devs.dzshn, EquicordDevs.Oggetto],
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
|
@ -159,37 +173,50 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "UNREAD_IMPORTANT:",
|
find: "UNREAD_IMPORTANT:",
|
||||||
predicate: () => settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,
|
predicate: () => settings.store.showMode !== ShowMode.LockIcon,
|
||||||
|
replacement: [
|
||||||
|
// Add the hidden eye icon if the channel is hidden
|
||||||
|
{
|
||||||
|
predicate: () => settings.store.showMode === ShowMode.EyeIconRight,
|
||||||
|
match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/,
|
||||||
|
replace: (m, channel) => `${m},$self.isHiddenChannel(${channel})?$self.EyeRightIcon():null`
|
||||||
|
},
|
||||||
|
// Add the hidden lock icon if the channel is hidden
|
||||||
|
{
|
||||||
|
predicate: () => settings.store.showMode === ShowMode.LockIconRight,
|
||||||
|
match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/,
|
||||||
|
replace: (m, channel) => `${m},$self.isHiddenChannel(${channel})?$self.LockRightIcon():null`
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: "UNREAD_IMPORTANT:",
|
||||||
|
predicate: () => settings.store.channelStyle === ChannelStyle.Muted || settings.store.channelStyle === ChannelStyle.MutedUnread,
|
||||||
replacement: [
|
replacement: [
|
||||||
// Make the channel appear as muted if it's hidden
|
// Make the channel appear as muted if it's hidden
|
||||||
{
|
{
|
||||||
match: /{channel:(\i),name:\i,muted:(\i).+?;/,
|
match: /{channel:(\i),name:\i,muted:(\i).+?;/,
|
||||||
replace: (m, channel, muted) => `${m}${muted}=$self.isHiddenChannel(${channel})?true:${muted};`
|
replace: (m, channel, muted) => `${m}${muted}=$self.isHiddenChannel(${channel})?true:${muted};`
|
||||||
},
|
},
|
||||||
// Add the hidden eye icon if the channel is hidden
|
|
||||||
{
|
|
||||||
match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/,
|
|
||||||
replace: (m, channel) => `${m},$self.isHiddenChannel(${channel})?$self.HiddenChannelIcon():null`
|
|
||||||
},
|
|
||||||
// Make voice channels also appear as muted if they are muted
|
// Make voice channels also appear as muted if they are muted
|
||||||
{
|
{
|
||||||
match: /(?<=\.wrapper:\i\.notInteractive,)(.+?)if\((\i)\)return (\i\.MUTED);/,
|
match: /(?<=\.wrapper:\i\.notInteractive,)(.+?)if\((\i)\)return (\i\.MUTED);/,
|
||||||
replace: (_, otherClasses, isMuted, mutedClassExpression) => `${isMuted}?${mutedClassExpression}:"",${otherClasses}if(${isMuted})return "";`
|
replace: (_, otherClasses, isMuted, mutedClassExpression) => `${isMuted}?${mutedClassExpression}:"",${otherClasses}if(${isMuted})return "";`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Make muted channels also appear as unread if hide unreads is false and the channel is hidden
|
||||||
|
predicate: () => settings.store.channelStyle === ChannelStyle.MutedUnread || settings.store.channelStyle === ChannelStyle.Unread,
|
||||||
|
match: /\.LOCKED;if\((?<={channel:(\i).+?)/,
|
||||||
|
replace: (m, channel) => `${m}!$self.isHiddenChannel(${channel})&&`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "UNREAD_IMPORTANT:",
|
find: "UNREAD_IMPORTANT:",
|
||||||
|
predicate: () => settings.store.channelStyle !== ChannelStyle.Unread && settings.store.channelStyle !== ChannelStyle.MutedUnread,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
|
||||||
// Make muted channels also appear as unread if hide unreads is false, using the HiddenIconWithMutedStyle and the channel is hidden
|
|
||||||
predicate: () => settings.store.hideUnreads === false && settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,
|
|
||||||
match: /\.LOCKED;if\((?<={channel:(\i).+?)/,
|
|
||||||
replace: (m, channel) => `${m}!$self.isHiddenChannel(${channel})&&`
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
// Hide unreads
|
// Hide unreads
|
||||||
predicate: () => settings.store.hideUnreads === true,
|
|
||||||
match: /{channel:(\i),name:\i,.+?unread:(\i).+?;/,
|
match: /{channel:(\i),name:\i,.+?unread:(\i).+?;/,
|
||||||
replace: (m, channel, unread) => `${m}${unread}=$self.isHiddenChannel(${channel})?false:${unread};`
|
replace: (m, channel, unread) => `${m}${unread}=$self.isHiddenChannel(${channel})?false:${unread};`
|
||||||
}
|
}
|
||||||
|
@ -543,7 +570,7 @@ export default definePlugin({
|
||||||
</svg>
|
</svg>
|
||||||
), { noop: true }),
|
), { noop: true }),
|
||||||
|
|
||||||
HiddenChannelIcon: ErrorBoundary.wrap(() => (
|
EyeRightIcon: ErrorBoundary.wrap(() => (
|
||||||
<Tooltip text="Hidden Channel">
|
<Tooltip text="Hidden Channel">
|
||||||
{({ onMouseLeave, onMouseEnter }) => (
|
{({ onMouseLeave, onMouseEnter }) => (
|
||||||
<svg
|
<svg
|
||||||
|
@ -560,5 +587,24 @@ export default definePlugin({
|
||||||
</svg>
|
</svg>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
), { noop: true }),
|
||||||
|
|
||||||
|
LockRightIcon: ErrorBoundary.wrap(() => (
|
||||||
|
<Tooltip text="Hidden Channel">
|
||||||
|
{({ onMouseLeave, onMouseEnter }) => (
|
||||||
|
<svg
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
className={ChannelListClasses.icon + " " + "shc-hidden-channel-icon"}
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
aria-hidden={true}
|
||||||
|
role="img"
|
||||||
|
>
|
||||||
|
<path className="shc-evenodd-fill-current-color" d="M17 11V7C17 4.243 14.756 2 12 2C9.242 2 7 4.243 7 7V11C5.897 11 5 11.896 5 13V20C5 21.103 5.897 22 7 22H17C18.103 22 19 21.103 19 20V13C19 11.896 18.103 11 17 11ZM12 18C11.172 18 10.5 17.328 10.5 16.5C10.5 15.672 11.172 15 12 15C12.828 15 13.5 15.672 13.5 16.5C13.5 17.328 12.828 18 12 18ZM15 11H9V7C9 5.346 10.346 4 12 4C13.654 4 15 5.346 15 7V11Z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
), { noop: true })
|
), { noop: true })
|
||||||
});
|
});
|
||||||
|
|
|
@ -943,7 +943,11 @@ export const EquicordDevs = Object.freeze({
|
||||||
Z1xus: {
|
Z1xus: {
|
||||||
name: "Z1xus",
|
name: "Z1xus",
|
||||||
id: 377450600797044746n,
|
id: 377450600797044746n,
|
||||||
}
|
},
|
||||||
|
Oggetto: {
|
||||||
|
name: "Oggetto",
|
||||||
|
id: 619203349954166804n,
|
||||||
|
},
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
// iife so #__PURE__ works correctly
|
// iife so #__PURE__ works correctly
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue