From e4947793bb9b08738fbc9a4d9499ea83b7c5636a Mon Sep 17 00:00:00 2001 From: fumiichan <35658068+fumiichan@users.noreply.github.com> Date: Fri, 31 Jan 2025 22:39:08 +0900 Subject: [PATCH] Merge changes from 'main' --- src/plugins/hideAttachments/index.tsx | 186 +++++++++++++++++++++++--- src/plugins/hideAttachments/types.ts | 44 ++++++ src/plugins/hideAttachments/utils.ts | 22 +++ src/utils/constants.ts | 4 + 4 files changed, 237 insertions(+), 19 deletions(-) create mode 100644 src/plugins/hideAttachments/types.ts create mode 100644 src/plugins/hideAttachments/utils.ts diff --git a/src/plugins/hideAttachments/index.tsx b/src/plugins/hideAttachments/index.tsx index fe9e68999..8108af51f 100644 --- a/src/plugins/hideAttachments/index.tsx +++ b/src/plugins/hideAttachments/index.tsx @@ -20,13 +20,17 @@ import "./styles.css"; import { get, set } from "@api/DataStore"; import { updateMessage } from "@api/MessageUpdater"; -import { migratePluginSettings } from "@api/Settings"; +import { definePluginSettings, migratePluginSettings } from "@api/Settings"; import { ImageInvisible, ImageVisible } from "@components/Icons"; import { Devs } from "@utils/constants"; import { classes } from "@utils/misc"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; import { ChannelStore } from "@webpack/common"; import { MessageSnapshot } from "@webpack/types"; +import { Embed } from "discord-types/general"; + +import { ILoadMessagesSuccessPayload, IMessage, IMessageCreatePayload, IMessageUpdatePayload } from "./types"; +import { isStringEmpty } from "./utils"; const KEY = "HideAttachments_HiddenIds"; @@ -41,10 +45,145 @@ const saveHiddenMessages = (ids: Set) => set(KEY, ids); migratePluginSettings("HideMedia", "HideAttachments"); +/** + * Toggle attachment/embed hiding + */ +const toggleHide = async (channelId: string, messageId: string): Promise => { + const ids = await getHiddenMessages(); + if (!ids.delete(messageId)) + ids.add(messageId); + + await saveHiddenMessages(ids); + updateMessage(channelId, messageId); +}; + +/** + * Determine if the message should be blocked according to user ID filter + * @param {Message} payload The message to be checked + * @param {string[]} userFilters List of user IDs to be checked + * @returns {boolean} + */ +const shouldHideByUserIdFilter = (payload: IMessage, userFilters: string[]): boolean => { + for (const id of userFilters) { + if (payload.author.id === id) { + return true; + } + } + + return false; +}; + +const shouldHideEmbed = (embeds: Embed[], domainList: string[]): boolean => { + for (const embed of embeds) { + if (!embed.url) { + continue; + } + + for (const domain of domainList) { + const host = URL.parse(embed.url)?.hostname ?? ""; + if (host.indexOf(domain) >= 0) { + return true; + } + } + } + + return false; +}; + +/** + * Determine if the message should be blocked according to domain list filter + * @param {Message} payload The message to be checked + * @param {string[]} domainList List of domains to be checked + * @returns {boolean} + */ +const shouldHideByDomainListFilter = (payload: IMessage, domainList: string[]): boolean => { + if (payload.embeds.length <= 0) { + return false; + } + + if (shouldHideEmbed(payload.embeds, domainList)) { + return true; + } + + // Check embeds from the forwarded messages + const hasReference = payload.message_reference && Array.isArray(payload.message_snapshots); + if (!hasReference) { + return false; + } + + for (const snapshot of payload.message_snapshots!) { + if (shouldHideEmbed(snapshot.message.embeds, domainList)) { + return true; + } + } + + return false; +}; + +/** + * Checks and hides the attachment/embed + * @param {Message} message The message to check + * @param {object} store The configuration values + */ +const checkAndHide = async (message: IMessage, store: typeof settings.store): Promise => { + if (!store.enableAutoHideAttachments) { + return; + } + + if (hiddenMessages.has(message.id)) { + return; + } + + const userFilters = isStringEmpty(store.filterUserList) + ? [] + : store.filterUserList.split(","); + if (shouldHideByUserIdFilter(message, userFilters)) { + await toggleHide(message.channel_id, message.id); + return; + } + + const domainFilters = isStringEmpty(store.filterDomainList) + ? [] + : store.filterDomainList.split(","); + if (shouldHideByDomainListFilter(message, domainFilters)) { + await toggleHide(message.channel_id, message.id); + return; + } + + // Forwarded messages + const hasReference = message.message_reference && Array.isArray(message.message_snapshots); + if (hasReference) { + for (const snapshot of message.message_snapshots!) { + if (shouldHideByDomainListFilter(snapshot.message, domainFilters)) { + await toggleHide(message.channel_id, message.id); + return; + } + } + } +}; + +const settings = definePluginSettings({ + enableAutoHideAttachments: { + type: OptionType.BOOLEAN, + description: "Enable auto hide attachments", + default: false, + }, + filterUserList: { + type: OptionType.STRING, + description: "Comma separated list of User IDs to automatically hide their attachments/embeds. (Requires auto hide to be ON)", + default: "", + }, + filterDomainList: { + type: OptionType.STRING, + description: "Comma separated list of domains to automatically hide their embeds. (Requires auto hide to be ON)", + default: "", + } +}); + export default definePlugin({ - name: "HideMedia", - description: "Hide attachments and embeds for individual messages via hover button", - authors: [Devs.Ven], + name: "HideAttachments", + description: "Hide attachments and Embeds for individual messages via hover button", + authors: [Devs.Ven, Devs.aiko], dependencies: ["MessageUpdaterAPI"], patches: [{ @@ -55,13 +194,15 @@ export default definePlugin({ } }], - renderMessagePopoverButton(msg) { + settings, + + renderMessagePopoverButton(msg: IMessage) { // @ts-ignore - discord-types lags behind discord. - const hasAttachmentsInShapshots = msg.messageSnapshots.some( - (snapshot: MessageSnapshot) => snapshot?.message.attachments.length + const hasAttachmentsInSnapshots = msg.messageSnapshots.some( + (snapshot: MessageSnapshot) => snapshot?.message.attachments.length || snapshot?.message.embeds.length ); - if (!msg.attachments.length && !msg.embeds.length && !msg.stickerItems.length && !hasAttachmentsInShapshots) return null; + if (!msg.attachments.length && !msg.embeds.length && !msg.stickerItems.length && !hasAttachmentsInSnapshots) return null; const isHidden = hiddenMessages.has(msg.id); @@ -70,7 +211,7 @@ export default definePlugin({ icon: isHidden ? ImageVisible : ImageInvisible, message: msg, channel: ChannelStore.getChannel(msg.channel_id), - onClick: () => this.toggleHide(msg.channel_id, msg.id) + onClick: () => toggleHide(msg.channel_id, msg.id) }; }, @@ -84,6 +225,22 @@ export default definePlugin({ ); }, + flux: { + async LOAD_MESSAGES_SUCCESS(payload: ILoadMessagesSuccessPayload) { + for (const message of payload.messages) { + await checkAndHide(message, settings.store); + } + }, + + async MESSAGE_CREATE({ message }: IMessageCreatePayload) { + await checkAndHide(message, settings.store); + }, + + async MESSAGE_UPDATE({ message }: IMessageUpdatePayload) { + await checkAndHide(message, settings.store); + } + }, + async start() { await getHiddenMessages(); }, @@ -94,14 +251,5 @@ export default definePlugin({ shouldHide(messageId: string) { return hiddenMessages.has(messageId); - }, - - async toggleHide(channelId: string, messageId: string) { - const ids = await getHiddenMessages(); - if (!ids.delete(messageId)) - ids.add(messageId); - - await saveHiddenMessages(ids); - updateMessage(channelId, messageId); } }); diff --git a/src/plugins/hideAttachments/types.ts b/src/plugins/hideAttachments/types.ts new file mode 100644 index 000000000..e6b25440f --- /dev/null +++ b/src/plugins/hideAttachments/types.ts @@ -0,0 +1,44 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 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 { Message } from "discord-types/general"; + +export interface ILoadMessagesSuccessPayload { + channelId: string; + messages: Array; +} + +export interface IMessage extends Message { + message_reference?: { + type: number; + channel_id: string; + message_id: string; + guild_id: string; + }, + message_snapshots?: { + message: Message; + }[] +} + +export interface IMessageCreatePayload { + message: IMessage; +} + +export interface IMessageUpdatePayload { + message: IMessage; +} diff --git a/src/plugins/hideAttachments/utils.ts b/src/plugins/hideAttachments/utils.ts new file mode 100644 index 000000000..2b0fcb6ef --- /dev/null +++ b/src/plugins/hideAttachments/utils.ts @@ -0,0 +1,22 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 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 . +*/ + +export function isStringEmpty (str: string) { + if (!str) return false; + return str.trim().length === 0; +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index e75825912..b95b036dd 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -579,6 +579,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "jamesbt365", id: 158567567487795200n, }, + aiko: { + name: "kima_riiiiiii", + id: 366434327761911808n + } } satisfies Record); // iife so #__PURE__ works correctly