From 42b9b2666609712359d1f55f37242c3e4940df06 Mon Sep 17 00:00:00 2001 From: Key Date: Sun, 28 Dec 2025 01:17:19 +0000 Subject: [PATCH] feat(MessageLoggerEnhanced): Add optional time-based cleanup (#603) * feat(MessageLoggerEnhanced): Add optional time-based cleanup - temporal cleanup preserves DMs and messages in the current channel by default - proper dm detection via ChannelStore (lmk if this is the wrong way to check) - add time limit and server message preservation to mle settings * feat(EquicordHelper): add feat of removed vencord plugin NoDefaultHangStatus it anchors to the default hang status and force it to set enableHangStatus to false. im not sure if it really belongs in EquicordHelper but it feels too small to be its own plugin, and it doesnt fit anywhere else that I can see. Let me know what you think. --- src/equicordplugins/equicordHelper/index.tsx | 17 +++++++++- .../LoggedMessageManager.ts | 21 +++++++++++-- .../messageLoggerEnhanced/db.ts | 31 +++++++++++++++++++ .../messageLoggerEnhanced/index.tsx | 24 ++++++++++---- .../messageLoggerEnhanced/settings.tsx | 12 +++++++ 5 files changed, 96 insertions(+), 9 deletions(-) diff --git a/src/equicordplugins/equicordHelper/index.tsx b/src/equicordplugins/equicordHelper/index.tsx index f571b043e..a736a719e 100644 --- a/src/equicordplugins/equicordHelper/index.tsx +++ b/src/equicordplugins/equicordHelper/index.tsx @@ -41,13 +41,19 @@ const settings = definePluginSettings({ description: "Discord hides your own activity buttons for some reason", restartNeeded: true, default: false, + }, + noDefaultHangStatus: { + type: OptionType.BOOLEAN, + description: "Disable the default hang status when joining voice channels", + restartNeeded: true, + default: false, } }); export default definePlugin({ name: "EquicordHelper", description: "Used to provide support, fix discord caused crashes, and other misc features.", - authors: [Devs.thororen, EquicordDevs.nyx, EquicordDevs.Naibuu], + authors: [Devs.thororen, EquicordDevs.nyx, EquicordDevs.Naibuu, EquicordDevs.keyages], required: true, settings, patches: [ @@ -120,6 +126,15 @@ export default definePlugin({ replace: "$& && false" } }, + // No Default Hang Status + { + find: ".CHILLING)", + predicate: () => settings.store.noDefaultHangStatus, + replacement: { + match: /{enableHangStatus:(\i),/, + replace: "{_enableHangStatus:$1=false," + } + }, // Always show open legacy settings ...[ ".DEVELOPER_SECTION,", diff --git a/src/equicordplugins/messageLoggerEnhanced/LoggedMessageManager.ts b/src/equicordplugins/messageLoggerEnhanced/LoggedMessageManager.ts index e83a30e61..5408349e9 100644 --- a/src/equicordplugins/messageLoggerEnhanced/LoggedMessageManager.ts +++ b/src/equicordplugins/messageLoggerEnhanced/LoggedMessageManager.ts @@ -17,18 +17,35 @@ */ import { Flogger, settings } from "."; -import { addMessageIDB, db, DBMessageStatus, deleteMessagesBulkIDB, getOldestMessagesIDB } from "./db"; +import { addMessageIDB, db, DBMessageStatus, deleteMessagesBulkIDB, getOlderThanTimestampForGuildsIDB, getOldestMessagesIDB } from "./db"; import { LoggedMessage, LoggedMessageJSON } from "./types"; import { cleanupMessage } from "./utils"; import { cacheMessageImages } from "./utils/saveImage"; -export const addMessage = async (message: LoggedMessage | LoggedMessageJSON, status: DBMessageStatus) => { +let lastCleanupTime = 0; +const CLEANUP_COOLDOWN = 60 * 1000; + +export const addMessage = async (message: LoggedMessage | LoggedMessageJSON, status: DBMessageStatus, currentChannelId?: string) => { if (settings.store.saveImages && status === DBMessageStatus.DELETED) await cacheMessageImages(message); const finalMessage = cleanupMessage(message); await addMessageIDB(finalMessage, status); + if (settings.store.timeBasedCleanupMinutes > 0) { + const now = Date.now(); + if (now - lastCleanupTime > CLEANUP_COOLDOWN) { + lastCleanupTime = now; + const cutoffTime = new Date(now - settings.store.timeBasedCleanupMinutes * 60 * 1000).toISOString(); + const oldGuildMessages = await getOlderThanTimestampForGuildsIDB(cutoffTime, currentChannelId, settings.store.preserveCurrentChannel); + + if (oldGuildMessages.length > 0) { + Flogger.info(`Deleting ${oldGuildMessages.length} old server messages older than ${settings.store.timeBasedCleanupMinutes} minutes`); + await deleteMessagesBulkIDB(oldGuildMessages.map(m => m.message_id)); + } + } + } + if (settings.store.messageLimit > 0) { const currentMessageCount = await db.count("messages"); if (currentMessageCount > settings.store.messageLimit) { diff --git a/src/equicordplugins/messageLoggerEnhanced/db.ts b/src/equicordplugins/messageLoggerEnhanced/db.ts index db22a62e5..21dcaa3a8 100644 --- a/src/equicordplugins/messageLoggerEnhanced/db.ts +++ b/src/equicordplugins/messageLoggerEnhanced/db.ts @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { ChannelStore } from "@webpack/common"; import { DBSchema, IDBPDatabase, openDB } from "idb"; import { LoggedMessageJSON } from "./types"; @@ -109,6 +110,36 @@ export async function getOldestMessagesIDB(limit: number) { return cacheRecords(await db.getAllFromIndex("messages", "by_timestamp", undefined, limit)); } +export async function getOlderThanTimestampIDB(timestamp: string) { + const tx = db.transaction("messages", "readonly"); + const { store } = tx; + const index = store.index("by_timestamp"); + + const cursor = await index.openCursor(IDBKeyRange.upperBound(timestamp)); + + if (!cursor) { + return []; + } + + const messages: DBMessageRecord[] = []; + for await (const c of cursor) { + messages.push(c.value); + } + + return cacheRecords(messages); +} + +export async function getOlderThanTimestampForGuildsIDB(timestamp: string, currentChannelId?: string, preserveCurrentChannel?: boolean) { + const allOldMessages = await getOlderThanTimestampIDB(timestamp); + return allOldMessages.filter(record => { + const { message } = record; + const channel = ChannelStore.getChannel(message.channel_id); + const isGuildMessage = channel?.guild_id != null; + const isCurrentChannel = preserveCurrentChannel && currentChannelId && message.channel_id === currentChannelId; + return isGuildMessage && !isCurrentChannel; + }); +} + export async function getDateStortedMessagesByStatusIDB(newest: boolean, limit: number, status: DBMessageStatus) { const tx = db.transaction("messages", "readonly"); const { store } = tx; diff --git a/src/equicordplugins/messageLoggerEnhanced/index.tsx b/src/equicordplugins/messageLoggerEnhanced/index.tsx index 5b4f8a536..db9a4238b 100644 --- a/src/equicordplugins/messageLoggerEnhanced/index.tsx +++ b/src/equicordplugins/messageLoggerEnhanced/index.tsx @@ -8,11 +8,11 @@ export const Native = getNative(); import "./styles.css"; -import { Devs } from "@utils/constants"; +import { Devs, EquicordDevs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { FluxDispatcher, MessageStore, UserStore } from "@webpack/common"; +import { FluxDispatcher, MessageStore, SelectedChannelStore, UserStore } from "@webpack/common"; import { LogsIcon, OpenLogsButton } from "./components/LogsButton"; import { openLogModal } from "./components/LogsModal"; @@ -89,7 +89,8 @@ async function messageDeleteHandler(payload: MessageDeletePayload & { isBulk: bo if (payload.isBulk) return message; - await addMessage(message, ghostPinged ? idb.DBMessageStatus.GHOST_PINGED : idb.DBMessageStatus.DELETED); + const currentChannelId = SelectedChannelStore.getChannelId(); + await addMessage(message, ghostPinged ? idb.DBMessageStatus.GHOST_PINGED : idb.DBMessageStatus.DELETED, currentChannelId); } finally { handledMessageIds.delete(payload.id); @@ -105,6 +106,17 @@ async function messageDeleteBulkHandler({ channelId, guildId, ids }: MessageDele } await idb.addMessagesBulkIDB(messages); + + if (messages.length > 0 && settings.store.timeBasedCleanupMinutes > 0) { + const currentChannelId = SelectedChannelStore.getChannelId(); + const cutoffTime = new Date(Date.now() - settings.store.timeBasedCleanupMinutes * 60 * 1000).toISOString(); + const oldGuildMessages = await idb.getOlderThanTimestampForGuildsIDB(cutoffTime, currentChannelId, settings.store.preserveCurrentChannel); + + if (oldGuildMessages.length > 0) { + Flogger.info(`Deleting ${oldGuildMessages.length} old server messages older than ${settings.store.timeBasedCleanupMinutes} minutes (bulk cleanup)`); + await idb.deleteMessagesBulkIDB(oldGuildMessages.map(m => m.message_id)); + } + } } async function messageUpdateHandler(payload: MessageUpdatePayload) { @@ -154,7 +166,8 @@ async function messageUpdateHandler(payload: MessageUpdatePayload) { if (message == null || message.channel_id == null || message.editHistory == null || message.editHistory.length === 0) return; // Flogger.log("ADDING MESSAGE (EDITED)", message, payload); - await addMessage(message, idb.DBMessageStatus.EDITED); + const currentChannelId = SelectedChannelStore.getChannelId(); + await addMessage(message, idb.DBMessageStatus.EDITED, currentChannelId); } function messageCreateHandler(payload: MessageCreatePayload) { @@ -228,7 +241,7 @@ async function processMessageFetch(response: FetchMessagesResponse) { export default definePlugin({ name: "MessageLoggerEnhanced", - authors: [Devs.Aria], + authors: [Devs.Aria, EquicordDevs.keyages], description: "G'day", dependencies: ["MessageLogger"], @@ -261,7 +274,6 @@ export default definePlugin({ replace: "$1childrenAccessories:arguments[0].childrenAccessories || null,childrenHeader:" } }, - // https://regex101.com/r/S3IVGm/1 // fix vidoes failing because there are no thumbnails { diff --git a/src/equicordplugins/messageLoggerEnhanced/settings.tsx b/src/equicordplugins/messageLoggerEnhanced/settings.tsx index 695a999da..fd54ebe2c 100644 --- a/src/equicordplugins/messageLoggerEnhanced/settings.tsx +++ b/src/equicordplugins/messageLoggerEnhanced/settings.tsx @@ -165,6 +165,18 @@ export const settings = definePluginSettings({ description: "Maximum number of messages to store in the cache. Older messages are deleted when the limit is reached. This helps reduce memory usage and improve performance. 0 means there is no limit", }, + timeBasedCleanupMinutes: { + default: 0, + type: OptionType.NUMBER, + description: "Automatically remove messages from servers that are older than this many minutes. Set to 0 to disable time-based cleanup.", + }, + + preserveCurrentChannel: { + default: true, + type: OptionType.BOOLEAN, + description: "When enabled, messages in your currently selected channel are not affected by time-based cleanup.", + }, + whitelistedIds: { default: "", type: OptionType.STRING,