Merge branch 'dev'

This commit is contained in:
thororen1234 2025-07-07 16:07:05 -04:00
commit 96684e1aca
No known key found for this signature in database
5 changed files with 251 additions and 5 deletions

View file

@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
### Extra included plugins
<details>
<summary>192 additional plugins</summary>
<summary>193 additional plugins</summary>
### All Platforms
@ -60,6 +60,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- Equissant by SomeAspy & thororen
- ExpandedUserTags by Cyn, TheSun, RyanCaoDev, LordElias, AutumnVN, hen
- ExportContacts by dat_insanity
- ExportMessages by veygax
- FakeProfileThemesAndEffects by ryan
- CopyProfileColors by Crxa
- FastDeleteChannels by thororen

View file

@ -0,0 +1,102 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { showNotice } from "@api/Notices";
import { Alerts, Toasts } from "@webpack/common";
import { Settings } from "Vencord";
function showErrorToast(message: string) {
Toasts.show({
message,
type: Toasts.Type.FAILURE,
id: Toasts.genId(),
options: {
position: Toasts.Position.BOTTOM
}
});
}
function restartPrompt(): Promise<boolean> {
return new Promise(resolve => {
Alerts.show({
title: "Restart Required",
body: (
<>
<p style={{ textAlign: "center" }}>
Some plugins require a restart to fully disable.
</p>
<p style={{ textAlign: "center" }}>Would you like to restart now?</p>
</>
),
confirmText: "Restart Now",
cancelText: "Later",
onConfirm: () => resolve(true),
onCancel: () => resolve(false),
});
});
}
export async function toggleEnabled(name: string) {
let restartNeeded = false;
function onRestartNeeded() {
restartNeeded = true;
}
async function beforeReturn(settings: any, wasEnabled: boolean) {
if (restartNeeded) {
const confirmed = await restartPrompt();
if (!confirmed) return false;
settings.enabled = !wasEnabled;
location.reload();
return true;
}
return true;
}
const plugin = Vencord.Plugins.plugins[name];
const settings = Settings.plugins[plugin.name];
const isEnabled = () => settings.enabled ?? false;
const wasEnabled = isEnabled();
if (!wasEnabled) {
const { restartNeeded, failures } = Vencord.Plugins.startDependenciesRecursive(plugin);
if (failures.length) {
console.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`);
showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null);
return false;
} else if (restartNeeded) {
settings.enabled = true;
onRestartNeeded();
return await beforeReturn(settings, wasEnabled);
}
}
if (plugin.patches?.length) {
onRestartNeeded();
return await beforeReturn(settings, wasEnabled);
}
if (wasEnabled && !plugin.started) {
settings.enabled = !wasEnabled;
return await beforeReturn(settings, wasEnabled);
}
const result = wasEnabled ? Vencord.Plugins.stopPlugin(plugin) : Vencord.Plugins.startPlugin(plugin);
if (!result) {
settings.enabled = false;
const msg = `Error while ${wasEnabled ? "stopping" : "starting"} plugin ${plugin.name}`;
console.error(msg);
showErrorToast(msg);
return false;
}
settings.enabled = !wasEnabled;
return await beforeReturn(settings, wasEnabled);
}

View file

@ -0,0 +1,113 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { showNotification } from "@api/Notifications";
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { showItemInFolder } from "@utils/native";
import definePlugin, { OptionType } from "@utils/types";
import { saveFile } from "@utils/web";
import { Menu } from "@webpack/common";
import { Message } from "discord-types/general";
const settings = definePluginSettings({
openFileAfterExport: {
type: OptionType.BOOLEAN,
description: "Open the exported file in the default file handler after export",
default: true
}
});
function formatMessage(message: Message) {
const { author } = message;
const timestamp = new Date(message.timestamp.toString()).toLocaleString();
let content = `[${timestamp}] ${author.username}`;
if (author.discriminator !== "0") {
content += `#${author.discriminator}`;
}
content += `: ${message.content}`;
if (message.attachments?.length > 0) {
content += "\n Attachments:";
message.attachments.forEach(attachment => {
content += `\n - ${attachment.filename} (${attachment.url})`;
});
}
if (message.embeds?.length > 0) {
content += "\n Embeds:";
message.embeds.forEach(embed => {
if (embed.rawTitle) content += `\n Title: ${embed.rawTitle}`;
if (embed.rawDescription) content += `\n Description: ${embed.rawDescription}`;
if (embed.url) content += `\n URL: ${embed.url}`;
});
}
return content;
}
async function exportMessage(message: Message) {
const timestamp = new Date(message.timestamp.toString()).toISOString().split("T")[0];
const filename = `message-${message.id}-${timestamp}.txt`;
const content = formatMessage(message);
try {
if (IS_DISCORD_DESKTOP) {
const data = new TextEncoder().encode(content);
const result = await DiscordNative.fileManager.saveWithDialog(data, filename);
if (result && settings.store.openFileAfterExport) {
showItemInFolder(result);
}
} else {
const file = new File([content], filename, { type: "text/plain" });
saveFile(file);
}
showNotification({
title: "Export Messages",
body: `Message exported successfully as ${filename}`,
icon: "📄"
});
} catch (error) {
showNotification({
title: "Export Messages",
body: "Failed to export message",
icon: "❌"
});
}
}
const messageContextMenuPatch = (children: Array<React.ReactElement<any> | null>, props: { message: Message; }) => {
const { message } = props;
if (!message) return;
children.push(
<Menu.MenuItem
id="export-message"
label="Export Message"
icon={() => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" />
</svg>
)}
action={() => exportMessage(message)}
/>
);
};
export default definePlugin({
name: "ExportMessages",
description: "Allows you to export any message to a file",
authors: [EquicordDevs.veygax],
settings,
contextMenus: {
"message": messageContextMenuPatch
}
});

View file

@ -22,6 +22,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { Link } from "@components/Link";
import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
import { toggleEnabled } from "@equicordplugins/equicordHelper/utils";
import { CONTRIB_ROLE_ID, Devs, DONOR_ROLE_ID, EQUCORD_HELPERS, EQUIBOP_CONTRIB_ROLE_ID, EQUICORD_TEAM, GUILD_ID, SUPPORT_CHANNEL_ID, SUPPORT_CHANNEL_IDS, VC_CONTRIB_ROLE_ID, VC_DONOR_ROLE_ID, VC_GUILD_ID, VC_REGULAR_ROLE_ID, VC_SUPPORT_CHANNEL_IDS, VENCORD_CONTRIB_ROLE_ID } from "@utils/constants";
import { sendMessage } from "@utils/discord";
import { Logger } from "@utils/Logger";
@ -295,11 +296,14 @@ export default definePlugin({
const shouldAddUpdateButton =
!IS_UPDATER_DISABLED
&& (
(props.channel.id === SUPPORT_CHANNEL_ID && equicordSupport)
)
&& ((props.channel.id === SUPPORT_CHANNEL_ID && equicordSupport))
&& props.message.content?.includes("update");
const matchedPlugins = Object.keys(Vencord.Plugins.plugins).filter(name => (props.message.content?.toLowerCase() ?? "").includes(name.toLowerCase()));
const matchedPlugin = matchedPlugins.sort((a, b) => b.length - a.length)[0];
const equicordGuild = ChannelStore.getChannel(props.channel.id)?.guild_id === GUILD_ID;
const shouldAddPluginButtons = equicordGuild && equicordSupport && matchedPlugin;
if (shouldAddUpdateButton) {
buttons.push(
<Button
@ -322,6 +326,28 @@ export default definePlugin({
);
}
if (shouldAddPluginButtons && matchedPlugin) {
const isEnabled = Vencord.Plugins.isPluginEnabled(matchedPlugin);
buttons.push(
<Button
key="vc-plugin-toggle"
color={isEnabled ? Button.Colors.RED : Button.Colors.GREEN}
onClick={async () => {
try {
const success = await toggleEnabled(matchedPlugin);
if (success) showToast(`${isEnabled ? "Disabled" : "Enabled"} ${matchedPlugin}`, Toasts.Type.SUCCESS);
} catch (e) {
new Logger(this.name).error("Error while toggling:", e);
showToast(`Failed to toggle ${matchedPlugin}`, Toasts.Type.FAILURE);
}
}}
>
{`${isEnabled ? "Disable" : "Enable"} ${matchedPlugin}`}
</Button>
);
}
if (props.channel.id === SUPPORT_CHANNEL_ID && PermissionStore.can(PermissionsBits.SEND_MESSAGES, props.channel)) {
if (props.message.content.includes("/equicord-debug") || props.message.content.includes("/equicord-plugins")) {
buttons.push(
@ -382,7 +408,7 @@ export default definePlugin({
<Card className={`vc-plugins-restart-card ${Margins.top8}`}>
Please do not private message plugin developers for support!
<br />
Instead, use the support channel: {Parser.parse("https://discord.com/channels/1173279886065029291/1173342942858055721")}
Instead, use the support channel: {Parser.parse("https://discord.com/channels/1173279886065029291/1297590739911573585")}
{!ChannelStore.getChannel(SUPPORT_CHANNEL_ID) && " (Click the link to join)"}
</Card>
);

View file

@ -1096,6 +1096,10 @@ export const EquicordDevs = Object.freeze({
name: "Suffocate",
id: 772601756776923187n
},
veygax: {
name: "veygax",
id: 1119938236245094521n
}
} satisfies Record<string, Dev>);
// iife so #__PURE__ works correctly