add chat moderation
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
const { getSupabaseClient, initializationPromise } = require('./supabaseClient');
|
||||
const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config');
|
||||
const { moderationText } = require('./chat-ai-agent/chat-moderation');
|
||||
const { getIo } = require('../../../io');
|
||||
|
||||
class ChatPollingHandler {
|
||||
constructor() {
|
||||
@@ -146,6 +149,16 @@ class ChatPollingHandler {
|
||||
const lastEventId = parseInt(last_event_id) || 0;
|
||||
const newEvents = eventQueue.filter(event => event.id > lastEventId);
|
||||
|
||||
// Логируем отправку событий клиенту
|
||||
if (newEvents.length > 0) {
|
||||
console.log(`📨 [Polling Server] Отправляем ${newEvents.length} событий клиенту ${user_id}`);
|
||||
newEvents.forEach(event => {
|
||||
if (event.event === 'message_updated') {
|
||||
console.log(`📨 [Polling Server] → Событие: ${event.event}, Сообщение ID: ${event.data?.message?.id}, Текст: "${event.data?.message?.text?.substring(0, 50)}${(event.data?.message?.text?.length || 0) > 50 ? '...' : ''}"`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
events: newEvents,
|
||||
@@ -605,6 +618,13 @@ class ChatPollingHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`📡 [Supabase Real-time] === ПОЛУЧЕНО НОВОЕ СООБЩЕНИЕ ===`);
|
||||
console.log(`📡 [Supabase Real-time] ID сообщения: ${newMessage.id}`);
|
||||
console.log(`📡 [Supabase Real-time] Чат ID: ${newMessage.chat_id}`);
|
||||
console.log(`📡 [Supabase Real-time] Пользователь ID: ${newMessage.user_id}`);
|
||||
console.log(`📡 [Supabase Real-time] Текст: "${newMessage.text?.substring(0, 100)}${(newMessage.text?.length || 0) > 100 ? '...' : ''}"`);
|
||||
console.log(`📡 [Supabase Real-time] Время: ${new Date().toISOString()}`);
|
||||
|
||||
// Получаем профиль пользователя
|
||||
const { data: userProfile, error: profileError } = await supabase
|
||||
.from('user_profiles')
|
||||
@@ -614,6 +634,8 @@ class ChatPollingHandler {
|
||||
|
||||
if (profileError) {
|
||||
console.error('❌ [Supabase] Ошибка получения профиля пользователя:', profileError);
|
||||
} else {
|
||||
console.log(`✅ [Supabase Real-time] Профиль пользователя получен: ${userProfile.full_name || 'No name'}`);
|
||||
}
|
||||
|
||||
// Объединяем сообщение с профилем
|
||||
@@ -623,13 +645,100 @@ class ChatPollingHandler {
|
||||
};
|
||||
|
||||
// Отправляем сообщение всем участникам чата
|
||||
console.log(`📤 [Supabase Real-time] Отправляем сообщение участникам чата ${newMessage.chat_id}...`);
|
||||
this.broadcastToChat(newMessage.chat_id, 'new_message', {
|
||||
message: messageWithProfile,
|
||||
timestamp: new Date()
|
||||
});
|
||||
console.log(`✅ [Supabase Real-time] Сообщение отправлено участникам чата`);
|
||||
|
||||
// === ЗАПУСК МОДЕРАЦИИ ===
|
||||
if (MODERATION_CONFIG.MODERATION_ENABLED) {
|
||||
console.log(`🛡️ [Supabase Real-time] Модерация включена - планируем проверку сообщения`);
|
||||
console.log(`🛡️ [Supabase Real-time] Задержка модерации: ${MODERATION_CONFIG.MODERATION_DELAY}мс`);
|
||||
|
||||
if (MODERATION_CONFIG.MODERATION_DELAY === 0) {
|
||||
console.log(`⚡ [Supabase Real-time] Мгновенная модерация - запускаем setImmediate`);
|
||||
setImmediate(() => {
|
||||
console.log(`⚡ [Supabase Real-time] setImmediate: запускаем модерацию сообщения ${newMessage.id}`);
|
||||
this.moderateMessage(newMessage.id, newMessage.text, newMessage.chat_id);
|
||||
});
|
||||
} else {
|
||||
console.log(`⏰ [Supabase Real-time] Отложенная модерация - устанавливаем setTimeout`);
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.log(`⏰ [Supabase Real-time] setTimeout: время пришло, запускаем модерацию сообщения ${newMessage.id}`);
|
||||
console.log(`⏰ [Supabase Real-time] Фактическое время: ${new Date().toISOString()}`);
|
||||
this.moderateMessage(newMessage.id, newMessage.text, newMessage.chat_id);
|
||||
}, MODERATION_CONFIG.MODERATION_DELAY);
|
||||
|
||||
console.log(`⏰ [Supabase Real-time] Timeout ID: ${timeoutId}`);
|
||||
console.log(`⏰ [Supabase Real-time] Ожидаемое время срабатывания: ${new Date(Date.now() + MODERATION_CONFIG.MODERATION_DELAY).toISOString()}`);
|
||||
}
|
||||
|
||||
console.log(`🛡️ [Supabase Real-time] Модерация запланирована для сообщения ${newMessage.id}`);
|
||||
} else {
|
||||
console.log(`🔓 [Supabase Real-time] Модерация отключена - сообщение не будет проверяться`);
|
||||
}
|
||||
|
||||
} catch (callbackError) {
|
||||
console.error('❌ [Supabase] Ошибка в обработчике сообщения:', callbackError);
|
||||
console.error('❌ [Supabase] Stack trace:', callbackError.stack);
|
||||
}
|
||||
}
|
||||
)
|
||||
.on(
|
||||
'postgres_changes',
|
||||
{
|
||||
event: 'UPDATE',
|
||||
schema: 'public',
|
||||
table: 'messages'
|
||||
},
|
||||
async (payload) => {
|
||||
try {
|
||||
const updatedMessage = payload.new;
|
||||
if (!updatedMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!updatedMessage.chat_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`🔄 [Supabase Real-time] === ПОЛУЧЕНО ОБНОВЛЕНИЕ СООБЩЕНИЯ ===`);
|
||||
console.log(`🔄 [Supabase Real-time] ID сообщения: ${updatedMessage.id}`);
|
||||
console.log(`🔄 [Supabase Real-time] Чат ID: ${updatedMessage.chat_id}`);
|
||||
console.log(`🔄 [Supabase Real-time] Пользователь ID: ${updatedMessage.user_id}`);
|
||||
console.log(`🔄 [Supabase Real-time] Обновленный текст: "${updatedMessage.text?.substring(0, 100)}${(updatedMessage.text?.length || 0) > 100 ? '...' : ''}"`);
|
||||
console.log(`🔄 [Supabase Real-time] Время обновления: ${new Date().toISOString()}`);
|
||||
|
||||
// Получаем профиль пользователя
|
||||
const { data: userProfile, error: profileError } = await supabase
|
||||
.from('user_profiles')
|
||||
.select('id, full_name, avatar_url')
|
||||
.eq('id', updatedMessage.user_id)
|
||||
.single();
|
||||
|
||||
if (profileError) {
|
||||
console.error('❌ [Supabase] Ошибка получения профиля пользователя:', profileError);
|
||||
}
|
||||
|
||||
// Объединяем сообщение с профилем
|
||||
const messageWithProfile = {
|
||||
...updatedMessage,
|
||||
user_profiles: userProfile || null
|
||||
};
|
||||
|
||||
// Отправляем обновление всем участникам чата
|
||||
console.log(`📤 [Supabase Real-time] Отправляем обновление участникам чата ${updatedMessage.chat_id}...`);
|
||||
this.broadcastToChat(updatedMessage.chat_id, 'message_updated', {
|
||||
message: messageWithProfile,
|
||||
timestamp: new Date()
|
||||
});
|
||||
console.log(`✅ [Supabase Real-time] Обновление отправлено участникам чата`);
|
||||
console.log(`📊 [Supabase Real-time] Событие: message_updated`);
|
||||
|
||||
} catch (callbackError) {
|
||||
console.error('❌ [Supabase] Ошибка в обработчике обновления сообщения:', callbackError);
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -654,6 +763,109 @@ class ChatPollingHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// Функция отложенной модерации сообщения
|
||||
async moderateMessage(messageId, messageText, chatId) {
|
||||
const moderationStartTime = Date.now();
|
||||
|
||||
try {
|
||||
console.log(`🔍 [Moderation] === НАЧАЛО МОДЕРАЦИИ СООБЩЕНИЯ ${messageId} ===`);
|
||||
console.log(`🔍 [Moderation] Chat ID: ${chatId}`);
|
||||
console.log(`🔍 [Moderation] Длина текста: ${messageText.length} символов`);
|
||||
console.log(`🔍 [Moderation] Превью текста: "${messageText.length > 100 ? messageText.substring(0, 100) + '...' : messageText}"`);
|
||||
console.log(`🔍 [Moderation] Время запуска: ${new Date().toISOString()}`);
|
||||
|
||||
// Вызываем функцию модерации
|
||||
console.log(`🔍 [Moderation] Передаем сообщение AI агенту для анализа...`);
|
||||
console.log(`🔍 [Moderation] Функция moderationText доступна: ${typeof moderationText}`);
|
||||
console.log(`🔍 [Moderation] Тип сообщения: ${typeof messageText}`);
|
||||
console.log(`🔍 [Moderation] Текст сообщения: "${messageText}"`);
|
||||
|
||||
let comment, isApproved, finalMessage;
|
||||
try {
|
||||
const result = await moderationText('', messageText);
|
||||
console.log(`🔍 [Moderation] Результат от AI агента получен:`, result);
|
||||
[comment, isApproved, finalMessage] = result;
|
||||
console.log(`🔍 [Moderation] Распакованные значения: comment="${comment}", isApproved=${isApproved}, finalMessage="${finalMessage}"`);
|
||||
} catch (moderationError) {
|
||||
console.error(`❌ [Moderation] Ошибка при вызове AI агента:`, moderationError);
|
||||
console.error(`❌ [Moderation] Stack trace:`, moderationError.stack);
|
||||
// В случае ошибки одобряем сообщение
|
||||
comment = '';
|
||||
isApproved = true;
|
||||
finalMessage = messageText;
|
||||
console.log(`⚠️ [Moderation] Используем fallback значения из-за ошибки`);
|
||||
}
|
||||
|
||||
const moderationTime = Date.now() - moderationStartTime;
|
||||
console.log(`📝 [Moderation] === РЕЗУЛЬТАТ МОДЕРАЦИИ СООБЩЕНИЯ ${messageId} ===`);
|
||||
console.log(`📝 [Moderation] Время модерации: ${moderationTime}мс`);
|
||||
console.log(`📝 [Moderation] Решение: ${isApproved ? '✅ ОДОБРЕНО' : '❌ ОТКЛОНЕНО'}`);
|
||||
console.log(`📝 [Moderation] Комментарий: "${comment || 'отсутствует'}"`);
|
||||
console.log(`📝 [Moderation] Финальный текст: "${finalMessage}"`);
|
||||
|
||||
if (isApproved) {
|
||||
console.log(`📝 [Moderation] Действие: сообщение остается без изменений`);
|
||||
} else {
|
||||
console.log(`📝 [Moderation] Действие: сообщение будет заменено в базе данных`);
|
||||
}
|
||||
|
||||
// Если сообщение не прошло модерацию, обновляем его в базе данных
|
||||
if (!isApproved) {
|
||||
console.log(`💾 [Moderation] Начинаем обновление сообщения в базе данных...`);
|
||||
|
||||
const supabase = getSupabaseClient();
|
||||
|
||||
// Сначала получаем информацию о сообщении для получения chat_id
|
||||
console.log(`💾 [Moderation] Получаем данные сообщения из базы...`);
|
||||
const { data: messageData, error: fetchError } = await supabase
|
||||
.from('messages')
|
||||
.select('chat_id, user_id')
|
||||
.eq('id', messageId)
|
||||
.single();
|
||||
|
||||
if (fetchError) {
|
||||
console.error(`❌ [Moderation] Ошибка получения данных сообщения ${messageId}:`, fetchError);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`💾 [Moderation] Данные получены. Chat ID: ${messageData.chat_id}, User ID: ${messageData.user_id}`);
|
||||
|
||||
// Обновляем текст сообщения
|
||||
console.log(`💾 [Moderation] Обновляем текст сообщения на: "${MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT}"`);
|
||||
const { data: updatedMessage, error } = await supabase
|
||||
.from('messages')
|
||||
.update({ text: MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT })
|
||||
.eq('id', messageId)
|
||||
.select('*')
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
console.error(`❌ [Moderation] Ошибка обновления сообщения ${messageId}:`, error);
|
||||
console.error(`❌ [Moderation] Детали ошибки:`, error);
|
||||
} else {
|
||||
console.log(`✅ [Moderation] Сообщение ${messageId} успешно обновлено в базе данных`);
|
||||
console.log(`✅ [Moderation] Старый текст заменен на: "${updatedMessage.text}"`);
|
||||
console.log(`✅ [Moderation] Время обновления: ${updatedMessage.updated_at || 'не указано'}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`✅ [Moderation] Сообщение ${messageId} прошло модерацию - никаких действий не требуется`);
|
||||
}
|
||||
|
||||
const totalTime = Date.now() - moderationStartTime;
|
||||
console.log(`🔍 [Moderation] === МОДЕРАЦИЯ СООБЩЕНИЯ ${messageId} ЗАВЕРШЕНА ===`);
|
||||
console.log(`🔍 [Moderation] Общее время процесса: ${totalTime}мс`);
|
||||
console.log(`🔍 [Moderation] Время завершения: ${new Date().toISOString()}`);
|
||||
|
||||
} catch (error) {
|
||||
const totalTime = Date.now() - moderationStartTime;
|
||||
console.error(`❌ [Moderation] === ОШИБКА МОДЕРАЦИИ СООБЩЕНИЯ ${messageId} ===`);
|
||||
console.error(`❌ [Moderation] Время до ошибки: ${totalTime}мс`);
|
||||
console.error(`❌ [Moderation] Тип ошибки: ${error.name || 'Unknown'}`);
|
||||
console.error(`❌ [Moderation] Сообщение ошибки: ${error.message || 'Unknown error'}`);
|
||||
console.error(`❌ [Moderation] Stack trace:`, error.stack);
|
||||
}
|
||||
}
|
||||
|
||||
// Получение статистики подключений
|
||||
getConnectionStats() {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user