139 lines
3.8 KiB
JavaScript
139 lines
3.8 KiB
JavaScript
/**
|
||
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||
* All rights reserved.
|
||
*
|
||
* This software is proprietary and confidential.
|
||
* Unauthorized copying, modification, or distribution is prohibited.
|
||
*
|
||
* For licensing inquiries: info@hb3-accelerator.com
|
||
* Website: https://hb3-accelerator.com
|
||
* GitHub: https://github.com/HB3-ACCELERATOR
|
||
*/
|
||
|
||
const crypto = require('crypto');
|
||
const logger = require('../utils/logger');
|
||
|
||
/**
|
||
* Сервис дедупликации сообщений
|
||
* Предотвращает обработку дублирующихся сообщений
|
||
*/
|
||
|
||
// Хранилище хешей обработанных сообщений (в памяти)
|
||
const processedMessages = new Map();
|
||
|
||
// Время жизни записи о сообщении (5 минут)
|
||
const MESSAGE_TTL = 5 * 60 * 1000;
|
||
|
||
/**
|
||
* Создать хеш сообщения
|
||
* @param {Object} messageData - Данные сообщения
|
||
* @returns {string} Хеш сообщения
|
||
*/
|
||
function createMessageHash(messageData) {
|
||
const hashData = {
|
||
userId: messageData.userId || messageData.user_id,
|
||
content: messageData.content,
|
||
channel: messageData.channel,
|
||
timestamp: Math.floor(Date.now() / 1000) // Округляем до секунд
|
||
};
|
||
|
||
return crypto
|
||
.createHash('sha256')
|
||
.update(JSON.stringify(hashData))
|
||
.digest('hex');
|
||
}
|
||
|
||
/**
|
||
* Проверить, было ли сообщение уже обработано
|
||
* @param {Object} messageData - Данные сообщения
|
||
* @returns {boolean} true если сообщение уже обрабатывалось
|
||
*/
|
||
function isDuplicate(messageData) {
|
||
const hash = createMessageHash(messageData);
|
||
|
||
if (processedMessages.has(hash)) {
|
||
const entry = processedMessages.get(hash);
|
||
const now = Date.now();
|
||
|
||
// Проверяем, не истек ли TTL
|
||
if (now - entry.timestamp < MESSAGE_TTL) {
|
||
logger.warn('[MessageDeduplication] Обнаружено дублирующееся сообщение:', hash);
|
||
return true;
|
||
} else {
|
||
// TTL истек, удаляем запись
|
||
processedMessages.delete(hash);
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Пометить сообщение как обработанное
|
||
* @param {Object} messageData - Данные сообщения
|
||
*/
|
||
function markAsProcessed(messageData) {
|
||
const hash = createMessageHash(messageData);
|
||
|
||
processedMessages.set(hash, {
|
||
timestamp: Date.now(),
|
||
messageData: {
|
||
userId: messageData.userId || messageData.user_id,
|
||
channel: messageData.channel
|
||
}
|
||
});
|
||
|
||
// Очищаем старые записи
|
||
cleanupOldEntries();
|
||
}
|
||
|
||
/**
|
||
* Очистить старые записи из хранилища
|
||
*/
|
||
function cleanupOldEntries() {
|
||
const now = Date.now();
|
||
let cleanedCount = 0;
|
||
|
||
for (const [hash, entry] of processedMessages.entries()) {
|
||
if (now - entry.timestamp > MESSAGE_TTL) {
|
||
processedMessages.delete(hash);
|
||
cleanedCount++;
|
||
}
|
||
}
|
||
|
||
if (cleanedCount > 0) {
|
||
logger.debug(`[MessageDeduplication] Очищено ${cleanedCount} старых записей`);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Получить статистику дедупликации
|
||
* @returns {Object}
|
||
*/
|
||
function getStats() {
|
||
return {
|
||
totalTracked: processedMessages.size,
|
||
ttl: MESSAGE_TTL
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Очистить все записи (для тестов)
|
||
*/
|
||
function clear() {
|
||
processedMessages.clear();
|
||
logger.info('[MessageDeduplication] Хранилище очищено');
|
||
}
|
||
|
||
// Периодическая очистка старых записей (каждую минуту)
|
||
setInterval(cleanupOldEntries, 60 * 1000);
|
||
|
||
module.exports = {
|
||
isDuplicate,
|
||
markAsProcessed,
|
||
getStats,
|
||
clear,
|
||
createMessageHash
|
||
};
|
||
|