MessageLogger: suppress deletes with an inert event instead of faking MESSAGE_UPDATE
This commit is contained in:
parent
1ff57e2d80
commit
31157788c5
3 changed files with 15 additions and 54 deletions
|
|
@ -3,7 +3,6 @@ import { storage } from "@vendetta/plugin";
|
|||
import Settings from "./ui/Settings";
|
||||
import { patchFluxIntercept, revertFakedMessages } from "./patches/fluxIntercept";
|
||||
import { patchRowStyling } from "./patches/rowStyling";
|
||||
import { patchMessageRecord } from "./patches/messageRecord";
|
||||
|
||||
const TAG = "[MessageLogger]";
|
||||
const cleanups: (() => void)[] = [];
|
||||
|
|
@ -35,9 +34,8 @@ export default {
|
|||
let patched = 0;
|
||||
if (patchFluxIntercept(cleanups)) patched++;
|
||||
if (patchRowStyling(cleanups)) patched++;
|
||||
if (patchMessageRecord(cleanups)) patched++;
|
||||
|
||||
console.log(TAG, `onLoad done - ${patched}/3 patches applied`);
|
||||
console.log(TAG, `onLoad done - ${patched}/2 patches applied`);
|
||||
},
|
||||
onUnload() {
|
||||
console.log(TAG, "onUnload");
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { addLogEntry } from "../lib/store";
|
|||
import type { FilterOptions } from "../lib/types";
|
||||
|
||||
const TAG = "[MessageLogger]";
|
||||
const FAKE_DELETE_FLAG = "__msgLoggerDeleted";
|
||||
const CLEANUP_FLAG = "__msgLoggerCleanup";
|
||||
|
||||
// A busy, high-traffic channel (flooding, spam, price-ticker bots editing the same message
|
||||
|
|
@ -93,8 +92,18 @@ function hasLoggableContent(message: any): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
function fakeUpdateFor(original: any): any {
|
||||
return { type: "MESSAGE_UPDATE", message: { ...original, [FAKE_DELETE_FLAG]: true } };
|
||||
// Any action type other than MESSAGE_DELETE stops the real removal - live-verified that
|
||||
// MESSAGE_EDIT_FAILED_AUTOMOD (the same technique meqativ/dumsane's NoDelete uses) is a genuine
|
||||
// no-op for a message not already in a pending-edit state: dispatching it left the cached
|
||||
// MessageRecord byte-for-byte identical. No need to reconstruct/spread the message into a fake
|
||||
// MESSAGE_UPDATE - that also avoids waking every other MESSAGE_UPDATE listener in the app (unread
|
||||
// counts, embed refresh, etc.) for something that isn't really an edit.
|
||||
function suppressionEventFor(original: any): any {
|
||||
return {
|
||||
type: "MESSAGE_EDIT_FAILED_AUTOMOD",
|
||||
messageData: { type: 0, message: { channelId: original.channel_id, messageId: original.id, content: original.content } },
|
||||
errorResponseBody: { code: 200000, message: "Deleted message kept inline by MessageLogger" },
|
||||
};
|
||||
}
|
||||
|
||||
function captureAndMaybeKeep(original: any, channelId: string, id: string): any | undefined {
|
||||
|
|
@ -112,7 +121,7 @@ function captureAndMaybeKeep(original: any, channelId: string, id: string): any
|
|||
|
||||
fakedMessages.set(id, channelId);
|
||||
evictOldest(fakedMessages, MAX_TRACKED_MESSAGES);
|
||||
return fakeUpdateFor(original);
|
||||
return suppressionEventFor(original);
|
||||
}
|
||||
|
||||
function handleSingleDelete(event: any): any {
|
||||
|
|
@ -148,7 +157,7 @@ function handleBulkDelete(event: any): any {
|
|||
const original = ChannelMessages?.get?.(channelId)?.get?.(id);
|
||||
if (!original) continue;
|
||||
try {
|
||||
FluxDispatcher.dispatch(fakeUpdateFor(original));
|
||||
FluxDispatcher.dispatch(suppressionEventFor(original));
|
||||
} catch (e: any) {
|
||||
console.error(TAG, "Deferred bulk-keep dispatch failed for", id, e?.message ?? e);
|
||||
}
|
||||
|
|
@ -166,7 +175,6 @@ function handleBulkDelete(event: any): any {
|
|||
function handleUpdate(event: any): void {
|
||||
const updated = event?.message;
|
||||
if (!updated?.id) return;
|
||||
if (updated[FAKE_DELETE_FLAG]) return; // our own fake-delete-as-update, not a real edit
|
||||
|
||||
const channelId = updated.channel_id ?? updated.channelId;
|
||||
const original = ChannelMessages?.get?.(channelId)?.get?.(updated.id);
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
import { findByName, findByProps } from "@vendetta/metro";
|
||||
import { after, instead } from "@vendetta/patcher";
|
||||
|
||||
const TAG = "[MessageLogger]";
|
||||
const FAKE_DELETE_FLAG = "__msgLoggerDeleted";
|
||||
|
||||
// Discord wraps raw message JSON into a MessageRecord class before it reaches row generation - a
|
||||
// plain custom field on the raw object doesn't reliably survive that wrap without this.
|
||||
export function patchMessageRecord(cleanups: (() => void)[]): boolean {
|
||||
const MessageRecordUtils = findByProps("updateMessageRecord", "createMessageRecord");
|
||||
const MessageRecord = findByName("MessageRecord", false);
|
||||
|
||||
if (!MessageRecordUtils || !MessageRecord) {
|
||||
console.warn(TAG, "MessageRecord module(s) not found - deleted messages may not render styled");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
cleanups.push(
|
||||
after("createMessageRecord", MessageRecordUtils, ([message]: any[], record: any) => {
|
||||
if (message?.[FAKE_DELETE_FLAG]) record[FAKE_DELETE_FLAG] = true;
|
||||
}),
|
||||
);
|
||||
|
||||
cleanups.push(
|
||||
instead("updateMessageRecord", MessageRecordUtils, ([oldRecord, newRecord]: any[], orig: any) => {
|
||||
if (newRecord?.[FAKE_DELETE_FLAG]) {
|
||||
return MessageRecordUtils.createMessageRecord(newRecord, oldRecord?.reactions);
|
||||
}
|
||||
return orig.apply(MessageRecordUtils, [oldRecord, newRecord]);
|
||||
}),
|
||||
);
|
||||
|
||||
cleanups.push(
|
||||
after("default", MessageRecord, ([props]: any[], record: any) => {
|
||||
if (props?.[FAKE_DELETE_FLAG]) record[FAKE_DELETE_FLAG] = true;
|
||||
}),
|
||||
);
|
||||
} catch (e: any) {
|
||||
console.warn(TAG, "Failed to patch MessageRecord:", e?.message ?? e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue