From 73a274db0d31b672e365bdad41a662cc8298fa34 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 5 Jul 2025 20:57:32 -0400 Subject: [PATCH 1/6] Fix Contributor DM Warning Card --- src/plugins/_core/supportHelper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index 7a016247e..bc594b365 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -382,7 +382,7 @@ export default definePlugin({ Please do not private message plugin developers for support!
- 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)"}
); From 830b5852f80f6c100fbfe01a8b094dfc34d7fe70 Mon Sep 17 00:00:00 2001 From: VeygaX <84923630+veygax@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:25:58 +0100 Subject: [PATCH 2/6] feat: add new ExportMessages plugin (#300) * feat: add new ExportMessages plugin * fix: update filename format in exportMessage function to use message ID cos it makes more sense --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com> --- src/equicordplugins/exportMessages/index.tsx | 115 +++++++++++++++++++ src/utils/constants.ts | 4 + 2 files changed, 119 insertions(+) create mode 100644 src/equicordplugins/exportMessages/index.tsx diff --git a/src/equicordplugins/exportMessages/index.tsx b/src/equicordplugins/exportMessages/index.tsx new file mode 100644 index 000000000..b65db8dac --- /dev/null +++ b/src/equicordplugins/exportMessages/index.tsx @@ -0,0 +1,115 @@ +/* + * 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 { findByPropsLazy } from "@webpack"; +import { Menu } from "@webpack/common"; +import { Message } from "discord-types/general"; + +const { saveFile } = findByPropsLazy("saveFile", "openFile"); + +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 { // for web + 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 | null>, props: { message: Message; }) => { + const { message } = props; + + if (!message) return; + + children.push( + ( + + + + )} + 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 + } +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 6c5e8fd4e..3ec7d7d89 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1096,6 +1096,10 @@ export const EquicordDevs = Object.freeze({ name: "Suffocate", id: 772601756776923187n }, + veygax: { + name: "veygax", + id: 1119938236245094521n + } } satisfies Record); // iife so #__PURE__ works correctly From 1f6a9ea8c143df5a354c1abbf20b9fc686b5f1e8 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Mon, 7 Jul 2025 07:27:44 -0400 Subject: [PATCH 3/6] Fix Readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 49eb937cb..b6b8db448 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-192 additional plugins +193 additional plugins ### 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 From ad4363d95ab4de8ca74be079117bb033b8a29e83 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:27:17 -0400 Subject: [PATCH 4/6] Fix ExportMessages --- src/equicordplugins/exportMessages/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/equicordplugins/exportMessages/index.tsx b/src/equicordplugins/exportMessages/index.tsx index b65db8dac..c4cf96d04 100644 --- a/src/equicordplugins/exportMessages/index.tsx +++ b/src/equicordplugins/exportMessages/index.tsx @@ -9,12 +9,10 @@ import { definePluginSettings } from "@api/Settings"; import { EquicordDevs } from "@utils/constants"; import { showItemInFolder } from "@utils/native"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { saveFile } from "@utils/web"; import { Menu } from "@webpack/common"; import { Message } from "discord-types/general"; -const { saveFile } = findByPropsLazy("saveFile", "openFile"); - const settings = definePluginSettings({ openFileAfterExport: { type: OptionType.BOOLEAN, @@ -66,7 +64,7 @@ async function exportMessage(message: Message) { if (result && settings.store.openFileAfterExport) { showItemInFolder(result); } - } else { // for web + } else { const file = new File([content], filename, { type: "text/plain" }); saveFile(file); } From 9d59730381cddfa10efc23969b6fb52300b56df3 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Mon, 7 Jul 2025 15:42:18 -0400 Subject: [PATCH 5/6] Enable/Disable Plugin Button For Helpers --- src/equicordplugins/equicordHelper/utils.tsx | 102 +++++++++++++++++++ src/plugins/_core/supportHelper.tsx | 37 ++++++- 2 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 src/equicordplugins/equicordHelper/utils.tsx diff --git a/src/equicordplugins/equicordHelper/utils.tsx b/src/equicordplugins/equicordHelper/utils.tsx new file mode 100644 index 000000000..5840844e2 --- /dev/null +++ b/src/equicordplugins/equicordHelper/utils.tsx @@ -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 { + return new Promise(resolve => { + Alerts.show({ + title: "Restart Required", + body: ( + <> +

+ Some plugins require a restart to fully disable. +

+

Would you like to restart now?

+ + ), + 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; + } 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 false; + } + + 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 await beforeReturn(settings, wasEnabled); + } + + settings.enabled = !wasEnabled; + return await beforeReturn(settings, wasEnabled); +} diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index bc594b365..2efdc8efc 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -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( + ); + } + 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( From 3df2582053b2a14852e400fc6c405f3dcb632ccc Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:06:11 -0400 Subject: [PATCH 6/6] Misc Fixes --- src/equicordplugins/equicordHelper/utils.tsx | 6 +++--- src/plugins/_core/supportHelper.tsx | 7 +------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/equicordplugins/equicordHelper/utils.tsx b/src/equicordplugins/equicordHelper/utils.tsx index 5840844e2..03ba6e8cd 100644 --- a/src/equicordplugins/equicordHelper/utils.tsx +++ b/src/equicordplugins/equicordHelper/utils.tsx @@ -69,7 +69,7 @@ export async function toggleEnabled(name: string) { if (failures.length) { console.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`); showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null); - return; + return false; } else if (restartNeeded) { settings.enabled = true; onRestartNeeded(); @@ -84,7 +84,7 @@ export async function toggleEnabled(name: string) { if (wasEnabled && !plugin.started) { settings.enabled = !wasEnabled; - return false; + return await beforeReturn(settings, wasEnabled); } const result = wasEnabled ? Vencord.Plugins.stopPlugin(plugin) : Vencord.Plugins.startPlugin(plugin); @@ -94,7 +94,7 @@ export async function toggleEnabled(name: string) { const msg = `Error while ${wasEnabled ? "stopping" : "starting"} plugin ${plugin.name}`; console.error(msg); showErrorToast(msg); - return await beforeReturn(settings, wasEnabled); + return false; } settings.enabled = !wasEnabled; diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index 2efdc8efc..23a197020 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -336,12 +336,7 @@ export default definePlugin({ onClick={async () => { try { const success = await toggleEnabled(matchedPlugin); - if (success) { - showToast( - `${isEnabled ? "Disabled" : "Enabled"} ${matchedPlugin}`, - Toasts.Type.SUCCESS - ); - } + 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);