9007f210ef
Introduce a rapidSpamTracker to detect same messages posted across channels within a short window and return prior message IDs for cleanup. Refactor spam detection to collect scannable text from message content, embeds and attachments, simplify role-ping and link heuristics, and mark spam when a role ping + link is duplicated across channels. Update index to use the tracker/collector, delete prior spam messages, improve moderation flow (ban → kick fallback), report clearer action/error labels, and warn about missing channel permissions. Update commands display text for detection/actions and bump .env.example MIN_ACCOUNT_AGE_DAYS default to 7.
47 lines
1.3 KiB
JavaScript
47 lines
1.3 KiB
JavaScript
const WINDOW_MS = 150_000; // 2.5 minutes
|
|
const recentPosts = new Map();
|
|
|
|
function normalizeKey(text) {
|
|
return text.toLowerCase().replace(/\s+/g, " ").trim().slice(0, 200);
|
|
}
|
|
|
|
export function trackCrossChannelMessage(guildId, userId, channelId, messageId, text) {
|
|
const normalized = normalizeKey(text);
|
|
if (!normalized) {
|
|
return { duplicateAcrossChannels: false, priorMessages: [] };
|
|
}
|
|
|
|
const key = `${guildId}:${userId}:${normalized}`;
|
|
const now = Date.now();
|
|
const existing = recentPosts.get(key);
|
|
|
|
if (existing && now - existing.firstSeen < WINDOW_MS) {
|
|
const priorMessages = [...existing.messages.entries()].map(([chId, msgId]) => ({
|
|
channelId: chId,
|
|
messageId: msgId,
|
|
}));
|
|
|
|
existing.channels.add(channelId);
|
|
existing.messages.set(channelId, messageId);
|
|
|
|
const duplicateAcrossChannels = existing.channels.size >= 2;
|
|
return { duplicateAcrossChannels, priorMessages: duplicateAcrossChannels ? priorMessages : [] };
|
|
}
|
|
|
|
recentPosts.set(key, {
|
|
channels: new Set([channelId]),
|
|
messages: new Map([[channelId, messageId]]),
|
|
firstSeen: now,
|
|
});
|
|
|
|
if (recentPosts.size > 5000) {
|
|
for (const [entryKey, entry] of recentPosts) {
|
|
if (now - entry.firstSeen > WINDOW_MS) {
|
|
recentPosts.delete(entryKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
return { duplicateAcrossChannels: false, priorMessages: [] };
|
|
}
|