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); } function dedupePriorMessages(priorMessages) { const seen = new Set(); return priorMessages.filter(({ channelId, messageId }) => { const entry = `${channelId}:${messageId}`; if (seen.has(entry)) return false; seen.add(entry); return true; }); } function trackCrossChannelPost(guildId, userId, channelId, messageId, keySuffix) { const normalized = normalizeKey(keySuffix); 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, }); pruneExpiredPosts(now); return { duplicateAcrossChannels: false, priorMessages: [] }; } function pruneExpiredPosts(now) { if (recentPosts.size <= 5000) return; for (const [entryKey, entry] of recentPosts) { if (now - entry.firstSeen > WINDOW_MS) { recentPosts.delete(entryKey); } } } export function trackCrossChannelMessage(guildId, userId, channelId, messageId, text) { return trackCrossChannelPost(guildId, userId, channelId, messageId, normalizeKey(text)); } export function trackCrossChannelKeys(guildId, userId, channelId, messageId, keys) { const uniqueKeys = [...new Set(keys.map((key) => normalizeKey(key)).filter(Boolean))]; if (uniqueKeys.length === 0) { return { duplicateAcrossChannels: false, priorMessages: [] }; } const trackers = uniqueKeys.map((key) => trackCrossChannelPost(guildId, userId, channelId, messageId, key) ); const duplicateAcrossChannels = trackers.some((tracker) => tracker.duplicateAcrossChannels); const priorMessages = duplicateAcrossChannels ? dedupePriorMessages(trackers.flatMap((tracker) => tracker.priorMessages)) : []; return { duplicateAcrossChannels, priorMessages }; } export { dedupePriorMessages };