From ce18e6516f71271da6e3d37b66b4836fea5ad198 Mon Sep 17 00:00:00 2001 From: reservedbytes <94451745+reservedbytes@users.noreply.github.com> Date: Sat, 19 Sep 2026 10:56:29 +0000 Subject: [PATCH 1/2] fix message logger losing history on reconnect --- src/plugins/messageLogger/index.tsx | 31 ++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 857da7b37f6..4a62c8241e6 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -31,7 +31,7 @@ import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; import { Message, MessageAttachment } from "@vencord/discord-types"; import { findCssClassesLazy } from "@webpack"; -import { AuthenticationStore, ChannelStore, FluxDispatcher, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common"; +import { AuthenticationStore, ChannelStore, FluxDispatcher, Menu, MessageStore, Parser, SelectedChannelStore, SnowflakeUtils, Timestamp, UserStore, useStateFromStores } from "@webpack/common"; import overlayStyle from "./deleteStyleOverlay.css?managed"; import textStyle from "./deleteStyleText.css?managed"; @@ -337,6 +337,27 @@ export default definePlugin({ return cache; }, + restoreMessages(cache: any, messages: MLMessage[], { isBefore, isAfter, hasMoreBefore, hasMoreAfter }: { isBefore?: boolean; isAfter?: boolean; hasMoreBefore?: boolean; hasMoreAfter?: boolean; }) { + const oldest = messages[0]?.id; + const newest = messages[messages.length - 1]?.id; + const fits = (id: string) => + (!isAfter && !hasMoreBefore || oldest != null && SnowflakeUtils.compare(id, oldest) > 0) && + (!isBefore && !hasMoreAfter || newest != null && SnowflakeUtils.compare(id, newest) < 0); + + const restored = new Map(); + cache.filter((m: MLMessage) => m.deleted).forEach((m: MLMessage) => restored.set(m.id, m)); + + const loaded = messages.map(m => { + restored.delete(m.id); + const history = cache.get(m.id)?.editHistory; + return history?.length && !m.editHistory?.length && !this.shouldIgnore(m, true) ? m.set("editHistory", history) : m; + }); + + return loaded + .concat([...restored.values()].filter(m => fits(m.id))) + .sort((a, b) => SnowflakeUtils.compare(a.id, b.id)); + }, + shouldIgnore(message: any, isEdit = false) { try { const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = settings.store; @@ -600,6 +621,14 @@ export default definePlugin({ match: /receiveMessage\((\i)\)\{/, replace: "$& $self.normalizeNonce($1);" } + }, + { + find: "this.truncateTop", + replacement: { + // add back deleted and edited messages discord dropped + match: /(?<=loadComplete\(\i\)\{.{0,400}?\i=)\i\(\)\(\i\)\.reverse\(\)\.map\(.{0,30}?\)\.value\(\)/, + replace: "$self.restoreMessages(this,$&,arguments[0])" + } } ] }); From 55c9beb131da0ddfb8e0472873b83b0565027b29 Mon Sep 17 00:00:00 2001 From: reservedbytes <94451745+reservedbytes@users.noreply.github.com> Date: Sat, 19 Sep 2026 11:24:29 +0000 Subject: [PATCH 2/2] fix message logger on unloaded channels --- src/plugins/messageLogger/index.tsx | 55 +++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 4a62c8241e6..d2755ebf2a7 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -30,7 +30,7 @@ import { Logger } from "@utils/Logger"; import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; import { Message, MessageAttachment } from "@vencord/discord-types"; -import { findCssClassesLazy } from "@webpack"; +import { findByCodeLazy, findByPropsLazy, findCssClassesLazy } from "@webpack"; import { AuthenticationStore, ChannelStore, FluxDispatcher, Menu, MessageStore, Parser, SelectedChannelStore, SnowflakeUtils, Timestamp, UserStore, useStateFromStores } from "@webpack/common"; import overlayStyle from "./deleteStyleOverlay.css?managed"; @@ -53,6 +53,10 @@ interface MLAttachment extends MessageAttachment { } const MessageClasses = findCssClassesLazy("edited", "communicationDisabled", "isSystemMessage"); +const ChannelMessages = findByPropsLazy("getOrCreate", "commit", "forEach"); +const createMessageRecord = findByCodeLazy(".createFromServer(", ".isBlockedForMessage", "messageReference:"); + +const uncachedMessages = new Map(); const settings = definePluginSettings({ deleteStyle: { @@ -122,6 +126,16 @@ const settings = definePluginSettings({ }, }); +function isLoggedChannel(channelId: string) { + const channel = ChannelStore.getChannel(channelId); + if (!channel) return false; + if (channel.isPrivate()) return true; + + let opened = false; + ChannelMessages.forEach(c => opened ||= c.hasFetched && ChannelStore.getChannel(c.channelId)?.guild_id === channel.guild_id); + return opened; +} + function addDeleteStyle() { if (settings.store.deleteStyle === "text") { enableStyle(textStyle); @@ -249,6 +263,38 @@ export default definePlugin({ "gdm-context": patchChannelContextMenu }, + flux: { + MESSAGE_CREATE({ channelId, message, optimistic }: { channelId: string; message: MLMessage; optimistic: boolean; }) { + if (optimistic || ChannelMessages.get(channelId)?.ready || !isLoggedChannel(channelId)) return; + + const messages = uncachedMessages.get(channelId); + if (messages) messages.push(message); + else uncachedMessages.set(channelId, [message]); + }, + MESSAGE_UPDATE({ message }: { message: MLMessage & { edited_timestamp: string; }; }) { + const stored = uncachedMessages.get(message.channel_id)?.find(m => m.id === message.id); + if (!stored) return; + + if (message.edited_timestamp && message.content !== stored.content) { + stored.editHistory = [...(stored.editHistory ?? []), { timestamp: new Date(message.edited_timestamp), content: stored.content }]; + } + Object.assign(stored, message); + }, + MESSAGE_DELETE({ channelId, id, mlDeleted }: { channelId: string; id: string; mlDeleted?: boolean; }) { + const messages = uncachedMessages.get(channelId); + const msg = messages?.find(m => m.id === id); + if (!messages || !msg) return; + + if (mlDeleted) messages.splice(messages.indexOf(msg), 1); + else msg.deleted = true; + }, + MESSAGE_DELETE_BULK({ channelId, ids }: { channelId: string; ids: string[]; }) { + uncachedMessages.get(channelId)?.forEach(m => { + if (ids.includes(m.id)) m.deleted = true; + }); + }, + }, + start() { addDeleteStyle(); }, @@ -344,15 +390,20 @@ export default definePlugin({ (!isAfter && !hasMoreBefore || oldest != null && SnowflakeUtils.compare(id, oldest) > 0) && (!isBefore && !hasMoreAfter || newest != null && SnowflakeUtils.compare(id, newest) < 0); + const stored = uncachedMessages.get(cache.channelId) ?? []; const restored = new Map(); cache.filter((m: MLMessage) => m.deleted).forEach((m: MLMessage) => restored.set(m.id, m)); + stored + .filter(m => m.deleted && !this.shouldIgnore(m)) + .forEach(m => restored.set(m.id, createMessageRecord({ ...m, attachments: m.attachments.map(a => ({ ...a, deleted: true })) }))); const loaded = messages.map(m => { restored.delete(m.id); - const history = cache.get(m.id)?.editHistory; + const history = stored.find(s => s.id === m.id)?.editHistory ?? cache.get(m.id)?.editHistory; return history?.length && !m.editHistory?.length && !this.shouldIgnore(m, true) ? m.set("editHistory", history) : m; }); + uncachedMessages.set(cache.channelId, stored.filter(m => !fits(m.id))); return loaded .concat([...restored.values()].filter(m => fits(m.id))) .sort((a, b) => SnowflakeUtils.compare(a.id, b.id));