From bd0b11dc4a63b5fc2ec0090da2c184bbed461efc Mon Sep 17 00:00:00 2001 From: DenAntonov Date: Sat, 14 Jun 2025 16:12:03 +0300 Subject: [PATCH 1/2] add chat moderation --- .../chat-ai-agent/chat-moderation.ts | 91 ++++++ .../chat-ai-agent/moderation-config.js | 18 ++ .../routers/kfu-m-24-1/sber_mobile/index.js | 24 ++ .../kfu-m-24-1/sber_mobile/messages.js | 285 +++++++++++++++++- .../kfu-m-24-1/sber_mobile/moderation.js | 118 ++++++++ .../kfu-m-24-1/sber_mobile/polling-chat.js | 212 +++++++++++++ 6 files changed, 743 insertions(+), 5 deletions(-) create mode 100644 server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts create mode 100644 server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js create mode 100644 server/routers/kfu-m-24-1/sber_mobile/moderation.js diff --git a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts new file mode 100644 index 0000000..ce269b7 --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts @@ -0,0 +1,91 @@ +import { Agent } from 'node:https'; +import { GigaChat } from "langchain-gigachat"; +import { z } from "zod"; + +const httpsAgent = new Agent({ + rejectUnauthorized: false, +}); + +const llm = new GigaChat({ + credentials: "MGIzODY1N2MtYzMwMS00N2I4LWI1YzQtM2U4NzAxZGI5NmMzOjJmNzcyYzBmLWU0NjUtNGNmZC1iMDM2LTRjNmY0N2JhNDdiOA==", + temperature: 0.2, + model: 'GigaChat-2', + httpsAgent, +}); + +// возвращаю комментарий + булево значение (удалять или нет) + финальный текст сообщения +const moderationLlm = llm.withStructuredOutput(z.object({ + comment: z.string(), + isApproved: z.boolean(), +}) as any) + +export const moderationText = async (title: string, body: string): Promise<[string, boolean, string]> => { + const startTime = Date.now(); + const messagePreview = body.length > 50 ? body.substring(0, 50) + '...' : body; + + console.log(`🤖 [AI Agent] Начинаем анализ сообщения: "${messagePreview}"`); + console.log(`🤖 [AI Agent] Длина сообщения: ${body.length} символов`); + console.log(`🤖 [AI Agent] Модель: GigaChat-2, Temperature: 0.2`); + + const prompt = ` + Ты модерируешь сообщения в чате. Твоя задача - проверить сообщение на нецензурную лексику, брань и неприемлемый контент. + + Сообщение: ${body} + + Твои задачи: + 1. Проверь сообщение на наличие нецензурной лексики, мата, ругательств и брани. + 2. Проверь на оскорбления, угрозы и агрессивное поведение. + 3. Проверь на спам и рекламу. + 4. Проверь на неприемлемый контент (дискриминация, экстремизм и т.д.). + + - Если сообщение не содержит запрещенного контента, оно одобряется (isApproved: true). + - Если сообщение содержит запрещенный контент, оно отклоняется (isApproved: false). + + Правила написания комментария: + - Если сообщение одобряется, оставь поле comment пустым. + - Если сообщение отклоняется, пиши комментарий со следующей формулировкой: + "Сообщение удалено. Причина: (укажи конкретную причину: нецензурная лексика, оскорбления, спам и т.д.)" + + ` + + try { + console.log(`🤖 [AI Agent] Отправляем запрос к GigaChat...`); + + const result = await moderationLlm.invoke(prompt); + + const processingTime = Date.now() - startTime; + console.log(`🤖 [AI Agent] Получен ответ от GigaChat за ${processingTime}мс`); + console.log(`🤖 [AI Agent] Результат анализа:`, { + isApproved: result.isApproved, + comment: result.comment || 'нет комментария', + hasComment: !!result.comment + }); + + // Дополнительная проверка + if(!result.isApproved && result.comment.trim() === '') { + console.log(`⚠️ [AI Agent] Сообщение отклонено, но комментарий пустой. Добавляем стандартный комментарий.`); + result.comment = 'Сообщение удалено. Причина: нарушение правил чата.' + } + + // Определяем итоговый текст сообщения + let finalMessage = body; + if (!result.isApproved) { + finalMessage = '[Удалено модератором]'; + console.log(`🚫 [AI Agent] Сообщение будет заменено на: "${finalMessage}"`); + } else { + console.log(`✅ [AI Agent] Сообщение одобрено, остается без изменений`); + } + + console.log(`🤖 [AI Agent] Анализ завершен. Общее время: ${Date.now() - startTime}мс`); + + return [result.comment, result.isApproved, finalMessage]; + + } catch (error) { + const processingTime = Date.now() - startTime; + console.error(`❌ [AI Agent] Ошибка при анализе сообщения (${processingTime}мс):`, error); + console.error(`❌ [AI Agent] Сообщение будет одобрено из-за ошибки модерации`); + + // В случае ошибки одобряем сообщение + return ['', true, body]; + } +}; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js new file mode 100644 index 0000000..eeab24b --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js @@ -0,0 +1,18 @@ +// Конфигурация системы модерации +const MODERATION_CONFIG = { + // Задержка перед запуском модерации (в миллисекундах) + MODERATION_DELAY: 3000, // 3 секунды + + // Включена ли система модерации + MODERATION_ENABLED: true, + + // Текст для замены заблокированных сообщений + BLOCKED_MESSAGE_TEXT: '[Удалено модератором]', + + // Логировать ли процесс модерации + ENABLE_MODERATION_LOGS: true +}; + +console.log(`⚙️ [Moderation Config] Конфигурация модерации загружена:`, MODERATION_CONFIG); + +module.exports = MODERATION_CONFIG; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/index.js b/server/routers/kfu-m-24-1/sber_mobile/index.js index 2fdc6bd..5e393c8 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/index.js +++ b/server/routers/kfu-m-24-1/sber_mobile/index.js @@ -1,4 +1,8 @@ const router = require('express').Router(); + +console.log(`🏗️ [Main Router] Загружаем основной роутер sber_mobile...`); +console.log(`🏗️ [Main Router] Время: ${new Date().toISOString()}`); + const authRouter = require('./auth'); const { supabaseRouter } = require('./supabaseClient'); const profileRouter = require('./profile'); @@ -8,7 +12,15 @@ const additionalServicesRouter = require('./additional_services'); const chatsRouter = require('./chats'); const camerasRouter = require('./cameras'); const ticketsRouter = require('./tickets'); + +console.log(`🏗️ [Main Router] Загружаем messagesRouter...`); const messagesRouter = require('./messages'); +console.log(`✅ [Main Router] messagesRouter загружен успешно`); + +console.log(`🏗️ [Main Router] Загружаем moderationRouter...`); +const moderationRouter = require('./moderation'); +console.log(`✅ [Main Router] moderationRouter загружен успешно`); + const utilityPaymentsRouter = require('./utility_payments'); const apartmentsRouter = require('./apartments'); const buildingsRouter = require('./buildings'); @@ -19,6 +31,8 @@ const supportRouter = require('./supportApi'); module.exports = router; +console.log(`🔗 [Main Router] Подключаем роутеры...`); + router.use('/auth', authRouter); router.use('/supabase', supabaseRouter); router.use('', profileRouter); @@ -28,7 +42,15 @@ router.use('', additionalServicesRouter); router.use('', chatsRouter); router.use('', camerasRouter); router.use('', ticketsRouter); + +console.log(`🔗 [Main Router] Подключаем messagesRouter...`); router.use('', messagesRouter); +console.log(`✅ [Main Router] messagesRouter подключен`); + +console.log(`🔗 [Main Router] Подключаем moderationRouter...`); +router.use('', moderationRouter); +console.log(`✅ [Main Router] moderationRouter подключен`); + router.use('', utilityPaymentsRouter); router.use('', apartmentsRouter); router.use('', buildingsRouter); @@ -36,5 +58,7 @@ router.use('', userApartmentsRouter); router.use('', avatarRouter); router.use('', supportRouter); +console.log(`🏗️ [Main Router] Все роутеры подключены успешно!`); + diff --git a/server/routers/kfu-m-24-1/sber_mobile/messages.js b/server/routers/kfu-m-24-1/sber_mobile/messages.js index f729679..25eb9fb 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/messages.js +++ b/server/routers/kfu-m-24-1/sber_mobile/messages.js @@ -2,6 +2,89 @@ const router = require('express').Router(); const { getSupabaseClient } = require('./supabaseClient'); const { getIo } = require('../../../io'); // Импортируем Socket.IO +console.log(`📦 [Messages Module] Загружаем модуль messages.js...`); +console.log(`📦 [Messages Module] Время загрузки: ${new Date().toISOString()}`); + +try { + const { moderationText } = require('./chat-ai-agent/chat-moderation'); // Импортируем функцию модерации + console.log(`✅ [Messages Module] Функция модерации загружена успешно`); +} catch (error) { + console.error(`❌ [Messages Module] Ошибка загрузки функции модерации:`, error); +} + +try { + const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); // Импортируем конфигурацию модерации + console.log(`✅ [Messages Module] Конфигурация модерации загружена:`, MODERATION_CONFIG); +} catch (error) { + console.error(`❌ [Messages Module] Ошибка загрузки конфигурации модерации:`, error); +} + +const { moderationText } = require('./chat-ai-agent/chat-moderation'); // Импортируем функцию модерации +const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); // Импортируем конфигурацию модерации + +console.log(`📦 [Messages Module] Модуль messages.js загружен полностью`); + +// Добавляем middleware для логирования всех запросов к messages роутеру + +// Тестовый эндпоинт для проверки работы роутера +router.get('/messages/test', (req, res) => { + console.log(`🧪 [Messages Test] Тестовый эндпоинт вызван`); + console.log(`🧪 [Messages Test] Время: ${new Date().toISOString()}`); + res.json({ + status: 'OK', + message: 'Messages router работает', + timestamp: new Date().toISOString(), + moderation_config: MODERATION_CONFIG + }); +}); + +// Тестовый эндпоинт для немедленной проверки AI агента +router.post('/messages/test-ai', async (req, res) => { + console.log(`🧪 [AI Test] === ТЕСТИРОВАНИЕ AI АГЕНТА ===`); + console.log(`🧪 [AI Test] Время: ${new Date().toISOString()}`); + + const { text } = req.body; + if (!text) { + return res.status(400).json({ error: 'text is required' }); + } + + console.log(`🧪 [AI Test] Тестируем текст: "${text}"`); + console.log(`🧪 [AI Test] Функция moderationText доступна: ${typeof moderationText}`); + + try { + console.log(`🧪 [AI Test] Вызываем AI агент напрямую...`); + const startTime = Date.now(); + const result = await moderationText('', text); + const endTime = Date.now(); + + console.log(`🧪 [AI Test] Результат от AI агента:`, result); + console.log(`🧪 [AI Test] Время выполнения: ${endTime - startTime}мс`); + + const [comment, isApproved, finalMessage] = result; + + res.json({ + success: true, + text: text, + comment: comment, + isApproved: isApproved, + finalMessage: finalMessage, + processingTime: endTime - startTime, + timestamp: new Date().toISOString() + }); + + } catch (error) { + console.error(`❌ [AI Test] Ошибка тестирования AI агента:`, error); + console.error(`❌ [AI Test] Stack:`, error.stack); + + res.status(500).json({ + success: false, + error: error.message, + stack: error.stack, + timestamp: new Date().toISOString() + }); + } +}); + // Получить все сообщения в чате с информацией о пользователе router.get('/messages', async (req, res) => { try { @@ -59,15 +142,40 @@ router.get('/messages', async (req, res) => { // Создать новое сообщение router.post('/messages', async (req, res) => { - const supabase = getSupabaseClient(); + console.log(`🚀 [Message Send] === ВХОД В POST /messages ЭНДПОИНТ ===`); + console.log(`🚀 [Message Send] Время входа: ${new Date().toISOString()}`); + console.log(`🚀 [Message Send] Request method: ${req.method}`); + console.log(`🚀 [Message Send] Request URL: ${req.originalUrl || req.url}`); + console.log(`🚀 [Message Send] Request body:`, JSON.stringify(req.body, null, 2)); + + console.log(`🔌 [Message Send] Получаем Supabase клиент...`); + let supabase; + try { + supabase = getSupabaseClient(); + console.log(`✅ [Message Send] Supabase клиент получен успешно`); + } catch (error) { + console.error(`❌ [Message Send] Ошибка получения Supabase клиента:`, error); + return res.status(500).json({ error: 'Database connection error' }); + } + const { chat_id, user_id, text } = req.body; + console.log(`📤 [Message Send] Получен запрос на отправку сообщения:`); + console.log(`📤 [Message Send] Chat ID: ${chat_id}`); + console.log(`📤 [Message Send] User ID: ${user_id}`); + console.log(`📤 [Message Send] Text length: ${text ? text.length : 0} символов`); + console.log(`📤 [Message Send] Text preview: "${text ? (text.length > 100 ? text.substring(0, 100) + '...' : text) : 'empty'}"`); + if (!chat_id || !user_id || !text) { + console.log(`❌ [Message Send] Отклонен: отсутствуют обязательные поля`); + console.log(`❌ [Message Send] chat_id: ${chat_id}, user_id: ${user_id}, text: ${text}`); return res.status(400).json({ error: 'chat_id, user_id, and text are required' }); } + console.log(`💾 [Message Send] Сохраняем сообщение в Supabase...`); + // Создаем сообщение const { data: newMessage, error } = await supabase .from('messages') @@ -75,14 +183,28 @@ router.post('/messages', async (req, res) => { .select('*') .single(); - if (error) return res.status(400).json({ error: error.message }); + if (error) { + console.error(`❌ [Message Send] Ошибка сохранения в Supabase:`, error); + return res.status(400).json({ error: error.message }); + } + + console.log(`✅ [Message Send] Сообщение сохранено. ID: ${newMessage.id}`); + console.log(`📅 [Message Send] Время создания: ${newMessage.created_at}`); + + console.log(`👤 [Message Send] Получаем профиль пользователя ${user_id}...`); // Получаем профиль пользователя - const { data: userProfile } = await supabase + const { data: userProfile, error: profileError } = await supabase .from('user_profiles') .select('id, full_name, avatar_url') .eq('id', user_id) .single(); + + if (profileError) { + console.log(`⚠️ [Message Send] Профиль пользователя не найден:`, profileError); + } else { + console.log(`✅ [Message Send] Профиль пользователя получен: ${userProfile.full_name || 'No name'}`); + } // Объединяем сообщение с профилем const data = { @@ -90,12 +212,167 @@ router.post('/messages', async (req, res) => { user_profiles: userProfile || null }; + console.log(`📊 [Message Send] Итоговые данные сообщения подготовлены`); + + // === МОДЕРАЦИЯ ЧЕРЕЗ SUPABASE REAL-TIME === + console.log(`🔄 [Message Send] Модерация будет выполняться через Supabase Real-time подписку`); + console.log(`🔄 [Message Send] Статус модерации: ${MODERATION_CONFIG.MODERATION_ENABLED ? 'включена' : 'отключена'}`); + console.log(`🔄 [Message Send] Задержка модерации: ${MODERATION_CONFIG.MODERATION_DELAY}мс`); + console.log(`🔄 [Message Send] После создания сообщения в БД сработает Supabase Real-time подписка в polling-chat.js`); + // Отправка через Socket.IO теперь происходит автоматически через Supabase Real-time подписку // Это предотвращает дублирование сообщений + + console.log(`✅ [Message Send] Сообщение успешно отправлено. Возвращаем ответ клиенту`); + console.log(`📤 [Message Send] === Процесс отправки сообщения завершен ===`); res.json(data); }); +// Функция отложенной модерации сообщения +async function 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 || 'не указано'}`); + + // Отправляем обновление через Socket.IO всем клиентам в чате + console.log(`📡 [Moderation] Начинаем отправку обновления через Socket.IO...`); + try { + const io = getIo(); + if (io) { + console.log(`📡 [Moderation] Socket.IO подключение активно`); + + // Получаем профиль пользователя для полной информации + console.log(`📡 [Moderation] Получаем профиль пользователя для обновления...`); + const { data: userProfile, error: profileError } = await supabase + .from('user_profiles') + .select('id, full_name, avatar_url') + .eq('id', messageData.user_id) + .single(); + + if (profileError) { + console.log(`⚠️ [Moderation] Ошибка получения профиля пользователя:`, profileError); + } else { + console.log(`✅ [Moderation] Профиль пользователя получен: ${userProfile.full_name || 'No name'}`); + } + + const messageWithProfile = { + ...updatedMessage, + user_profiles: userProfile || null + }; + + console.log(`📡 [Moderation] Отправляем обновление в комнату chat_${messageData.chat_id}...`); + io.to(`chat_${messageData.chat_id}`).emit('message_updated', messageWithProfile); + console.log(`📤 [Moderation] ✅ Обновление сообщения отправлено в чат ${messageData.chat_id}`); + console.log(`📤 [Moderation] Событие: message_updated`); + console.log(`📤 [Moderation] Получатели: все участники чата ${messageData.chat_id}`); + } else { + console.log(`⚠️ [Moderation] Socket.IO подключение недоступно`); + } + } catch (socketError) { + console.error(`❌ [Moderation] Ошибка отправки через Socket.IO:`, socketError); + console.error(`❌ [Moderation] Детали ошибки Socket.IO:`, socketError.message); + } + } + } 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] Полная ошибка:`, error); + console.error(`❌ [Moderation] Стек ошибки:`, error.stack); + console.error(`❌ [Moderation] === КОНЕЦ ОБРАБОТКИ ОШИБКИ ===`); + } +} + // Получить конкретное сообщение router.get('/messages/:message_id', async (req, res) => { const supabase = getSupabaseClient(); @@ -203,6 +480,4 @@ router.delete('/messages/:message_id', async (req, res) => { res.json({ success: true, message: 'Message deleted successfully' }); }); - - module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/moderation.js b/server/routers/kfu-m-24-1/sber_mobile/moderation.js new file mode 100644 index 0000000..a3be652 --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/moderation.js @@ -0,0 +1,118 @@ +const router = require('express').Router(); +const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); +const { moderationText } = require('./chat-ai-agent/chat-moderation'); + +// Получить текущие настройки модерации +router.get('/moderation/config', (req, res) => { + console.log(`⚙️ [Config] Запрос на получение конфигурации модерации`); + console.log(`⚙️ [Config] Текущие настройки:`, MODERATION_CONFIG); + res.json(MODERATION_CONFIG); +}); + +// Обновить настройки модерации +router.post('/moderation/config', (req, res) => { + console.log(`⚙️ [Config] === ОБНОВЛЕНИЕ КОНФИГУРАЦИИ МОДЕРАЦИИ ===`); + console.log(`⚙️ [Config] Время запроса: ${new Date().toISOString()}`); + console.log(`⚙️ [Config] Полученные данные:`, req.body); + + const oldConfig = { ...MODERATION_CONFIG }; + const { MODERATION_DELAY, MODERATION_ENABLED, BLOCKED_MESSAGE_TEXT, ENABLE_MODERATION_LOGS } = req.body; + + const changes = []; + + if (MODERATION_DELAY !== undefined) { + const newValue = parseInt(MODERATION_DELAY); + console.log(`⚙️ [Config] Обновляем MODERATION_DELAY: ${MODERATION_CONFIG.MODERATION_DELAY} -> ${newValue}`); + MODERATION_CONFIG.MODERATION_DELAY = newValue; + changes.push(`MODERATION_DELAY: ${oldConfig.MODERATION_DELAY} -> ${newValue}`); + } + if (MODERATION_ENABLED !== undefined) { + const newValue = Boolean(MODERATION_ENABLED); + console.log(`⚙️ [Config] Обновляем MODERATION_ENABLED: ${MODERATION_CONFIG.MODERATION_ENABLED} -> ${newValue}`); + MODERATION_CONFIG.MODERATION_ENABLED = newValue; + changes.push(`MODERATION_ENABLED: ${oldConfig.MODERATION_ENABLED} -> ${newValue}`); + } + if (BLOCKED_MESSAGE_TEXT !== undefined) { + const newValue = String(BLOCKED_MESSAGE_TEXT); + console.log(`⚙️ [Config] Обновляем BLOCKED_MESSAGE_TEXT: "${MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT}" -> "${newValue}"`); + MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT = newValue; + changes.push(`BLOCKED_MESSAGE_TEXT: "${oldConfig.BLOCKED_MESSAGE_TEXT}" -> "${newValue}"`); + } + if (ENABLE_MODERATION_LOGS !== undefined) { + const newValue = Boolean(ENABLE_MODERATION_LOGS); + console.log(`⚙️ [Config] Обновляем ENABLE_MODERATION_LOGS: ${MODERATION_CONFIG.ENABLE_MODERATION_LOGS} -> ${newValue}`); + MODERATION_CONFIG.ENABLE_MODERATION_LOGS = newValue; + changes.push(`ENABLE_MODERATION_LOGS: ${oldConfig.ENABLE_MODERATION_LOGS} -> ${newValue}`); + } + + console.log(`⚙️ [Config] === ИТОГИ ОБНОВЛЕНИЯ ===`); + console.log(`⚙️ [Config] Количество изменений: ${changes.length}`); + if (changes.length > 0) { + changes.forEach((change, index) => { + console.log(`⚙️ [Config] ${index + 1}. ${change}`); + }); + } else { + console.log(`⚙️ [Config] Изменений не было внесено`); + } + console.log(`⚙️ [Config] Новая конфигурация:`, MODERATION_CONFIG); + console.log(`⚙️ [Config] === ОБНОВЛЕНИЕ ЗАВЕРШЕНО ===`); + + res.json({ + success: true, + message: 'Настройки модерации обновлены', + changes: changes, + config: MODERATION_CONFIG + }); +}); + +// Тестовый эндпоинт для проверки модерации +router.post('/moderation/test', async (req, res) => { + const testStartTime = Date.now(); + + try { + const { text } = req.body; + + console.log(`🧪 [Moderation Test] === НАЧАЛО ТЕСТИРОВАНИЯ МОДЕРАЦИИ ===`); + console.log(`🧪 [Moderation Test] Время запуска: ${new Date().toISOString()}`); + console.log(`🧪 [Moderation Test] Текст для тестирования: "${text ? (text.length > 100 ? text.substring(0, 100) + '...' : text) : 'не указан'}"`); + console.log(`🧪 [Moderation Test] Длина текста: ${text ? text.length : 0} символов`); + + if (!text) { + console.log(`❌ [Moderation Test] Отклонен: текст не предоставлен`); + return res.status(400).json({ error: 'text is required' }); + } + + console.log(`🧪 [Moderation Test] Отправляем текст на модерацию...`); + const [comment, isApproved, finalMessage] = await moderationText('', text); + + const testTime = Date.now() - testStartTime; + console.log(`🧪 [Moderation Test] === РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ ===`); + console.log(`🧪 [Moderation Test] Время тестирования: ${testTime}мс`); + console.log(`🧪 [Moderation Test] Результат: ${isApproved ? '✅ ОДОБРЕНО' : '❌ ОТКЛОНЕНО'}`); + console.log(`🧪 [Moderation Test] Комментарий: "${comment || 'отсутствует'}"`); + console.log(`🧪 [Moderation Test] Финальное сообщение: "${finalMessage}"`); + console.log(`🧪 [Moderation Test] === ТЕСТИРОВАНИЕ ЗАВЕРШЕНО ===`); + + res.json({ + original_text: text, + is_approved: isApproved, + comment: comment, + final_message: finalMessage, + processing_time_ms: testTime + }); + } catch (error) { + const testTime = Date.now() - testStartTime; + console.error(`❌ [Moderation Test] === ОШИБКА ТЕСТИРОВАНИЯ ===`); + console.error(`❌ [Moderation Test] Время до ошибки: ${testTime}мс`); + console.error(`❌ [Moderation Test] Ошибка:`, error); + console.error(`❌ [Moderation Test] === КОНЕЦ ОБРАБОТКИ ОШИБКИ ===`); + + res.status(500).json({ + error: 'Moderation test failed', + details: error.message, + processing_time_ms: testTime + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js b/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js index db528ca..eb7c4df 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js +++ b/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js @@ -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 { From 5665c4bf1e20bdbe5db0c1964929afc1a8a668ea Mon Sep 17 00:00:00 2001 From: DenAntonov Date: Sat, 14 Jun 2025 23:35:48 +0300 Subject: [PATCH 2/2] code refactoring and agent improvement --- .../chat-ai-agent/chat-moderation.ts | 157 +++++------ .../sber_mobile/chat-ai-agent/gigachat.ts | 18 ++ .../chat-ai-agent/moderation-config.js | 4 +- .../kfu-m-24-1/sber_mobile/get-constants.js | 3 +- .../routers/kfu-m-24-1/sber_mobile/index.js | 12 - .../kfu-m-24-1/sber_mobile/messages.js | 248 ------------------ .../kfu-m-24-1/sber_mobile/moderation.js | 67 +---- .../kfu-m-24-1/sber_mobile/polling-chat.js | 86 ++---- .../kfu-m-24-1/sber_mobile/supabaseClient.js | 3 - 9 files changed, 111 insertions(+), 487 deletions(-) create mode 100644 server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/gigachat.ts diff --git a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts index ce269b7..b89f8d5 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts +++ b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts @@ -1,91 +1,78 @@ -import { Agent } from 'node:https'; -import { GigaChat } from "langchain-gigachat"; import { z } from "zod"; +import gigachat from './gigachat'; -const httpsAgent = new Agent({ - rejectUnauthorized: false, -}); +export interface ModerationResult { + comment: string; + isApproved: boolean; + success: boolean; + error?: string; +} -const llm = new GigaChat({ - credentials: "MGIzODY1N2MtYzMwMS00N2I4LWI1YzQtM2U4NzAxZGI5NmMzOjJmNzcyYzBmLWU0NjUtNGNmZC1iMDM2LTRjNmY0N2JhNDdiOA==", - temperature: 0.2, - model: 'GigaChat-2', - httpsAgent, -}); +export class ChatModerationAgent { + private moderationLlm: any; -// возвращаю комментарий + булево значение (удалять или нет) + финальный текст сообщения -const moderationLlm = llm.withStructuredOutput(z.object({ - comment: z.string(), - isApproved: z.boolean(), -}) as any) - -export const moderationText = async (title: string, body: string): Promise<[string, boolean, string]> => { - const startTime = Date.now(); - const messagePreview = body.length > 50 ? body.substring(0, 50) + '...' : body; - - console.log(`🤖 [AI Agent] Начинаем анализ сообщения: "${messagePreview}"`); - console.log(`🤖 [AI Agent] Длина сообщения: ${body.length} символов`); - console.log(`🤖 [AI Agent] Модель: GigaChat-2, Temperature: 0.2`); - - const prompt = ` - Ты модерируешь сообщения в чате. Твоя задача - проверить сообщение на нецензурную лексику, брань и неприемлемый контент. - - Сообщение: ${body} - - Твои задачи: - 1. Проверь сообщение на наличие нецензурной лексики, мата, ругательств и брани. - 2. Проверь на оскорбления, угрозы и агрессивное поведение. - 3. Проверь на спам и рекламу. - 4. Проверь на неприемлемый контент (дискриминация, экстремизм и т.д.). - - - Если сообщение не содержит запрещенного контента, оно одобряется (isApproved: true). - - Если сообщение содержит запрещенный контент, оно отклоняется (isApproved: false). - - Правила написания комментария: - - Если сообщение одобряется, оставь поле comment пустым. - - Если сообщение отклоняется, пиши комментарий со следующей формулировкой: - "Сообщение удалено. Причина: (укажи конкретную причину: нецензурная лексика, оскорбления, спам и т.д.)" - - ` - - try { - console.log(`🤖 [AI Agent] Отправляем запрос к GigaChat...`); - - const result = await moderationLlm.invoke(prompt); - - const processingTime = Date.now() - startTime; - console.log(`🤖 [AI Agent] Получен ответ от GigaChat за ${processingTime}мс`); - console.log(`🤖 [AI Agent] Результат анализа:`, { - isApproved: result.isApproved, - comment: result.comment || 'нет комментария', - hasComment: !!result.comment - }); - - // Дополнительная проверка - if(!result.isApproved && result.comment.trim() === '') { - console.log(`⚠️ [AI Agent] Сообщение отклонено, но комментарий пустой. Добавляем стандартный комментарий.`); - result.comment = 'Сообщение удалено. Причина: нарушение правил чата.' - } - - // Определяем итоговый текст сообщения - let finalMessage = body; - if (!result.isApproved) { - finalMessage = '[Удалено модератором]'; - console.log(`🚫 [AI Agent] Сообщение будет заменено на: "${finalMessage}"`); - } else { - console.log(`✅ [AI Agent] Сообщение одобрено, остается без изменений`); - } - - console.log(`🤖 [AI Agent] Анализ завершен. Общее время: ${Date.now() - startTime}мс`); - - return [result.comment, result.isApproved, finalMessage]; - - } catch (error) { - const processingTime = Date.now() - startTime; - console.error(`❌ [AI Agent] Ошибка при анализе сообщения (${processingTime}мс):`, error); - console.error(`❌ [AI Agent] Сообщение будет одобрено из-за ошибки модерации`); - - // В случае ошибки одобряем сообщение - return ['', true, body]; + constructor(GIGA_AUTH) { + // Создаем структурированный вывод для модерации + this.moderationLlm = gigachat(GIGA_AUTH).withStructuredOutput(z.object({ + comment: z.string(), + isApproved: z.boolean(), + }) as any); } + + private getSystemPrompt(): string { + return `Ты модерируешь сообщения в чате. Твоя задача - проверить сообщение на нецензурную лексику, брань и неприемлемый контент. + +Твои задачи: +1. Проверь сообщение на наличие нецензурной лексики, мата, ругательств и брани. +2. Проверь на оскорбления, угрозы и агрессивное поведение. +3. Проверь на спам и рекламу. +4. Проверь на неприемлемый контент (дискриминация, экстремизм и т.д.). + +- Если сообщение не содержит запрещенного контента, оно одобряется (isApproved: true). +- Если сообщение содержит запрещенный контент, оно отклоняется (isApproved: false). + +Правила написания комментария: +- Если сообщение одобряется, оставь поле comment пустым. +- Если сообщение отклоняется, пиши комментарий со следующей формулировкой: + "Сообщение удалено. Причина: (укажи конкретную причину: нецензурная лексика, оскорбления, спам и т.д.)"`; + } + + public async moderateMessage(message: string): Promise { + try { + const prompt = `${this.getSystemPrompt()} + +Сообщение: ${message}`; + + const result = await this.moderationLlm.invoke(prompt); + + // Дополнительная проверка + if (!result.isApproved && result.comment.trim() === '') { + result.comment = 'Сообщение удалено. Причина: нарушение правил чата.'; + } + + return { + comment: result.comment, + isApproved: result.isApproved, + success: true + }; + + } catch (error) { + console.error('❌ [Chat Moderation] Ошибка при модерации:', error); + + // В случае ошибки одобряем сообщение + return { + comment: '', + isApproved: true, + success: false, + error: error instanceof Error ? error.message : 'Неизвестная ошибка' + }; + } + } +} + +// Экспортируем функцию для обратной совместимости +export const moderationText = async (title: string, body: string, GIGA_AUTH): Promise<[string, boolean, string]> => { + const agent = new ChatModerationAgent(GIGA_AUTH); + const result = await agent.moderateMessage(body); + return [result.comment, result.isApproved, body]; }; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/gigachat.ts b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/gigachat.ts new file mode 100644 index 0000000..609020a --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/gigachat.ts @@ -0,0 +1,18 @@ +import { Agent } from 'node:https'; +import { GigaChat } from 'langchain-gigachat'; + +const httpsAgent = new Agent({ + rejectUnauthorized: false, +}); + +// Получаем GIGA_AUTH из переменной окружения (устанавливается в get-constants.js) +export const gigachat = (GIGA_AUTH) => new + GigaChat({ + model: 'GigaChat-2', + scope: 'GIGACHAT_API_PERS', + streaming: false, + credentials: GIGA_AUTH, + httpsAgent +}); + +export default gigachat; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js index eeab24b..c4efec6 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js +++ b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js @@ -1,7 +1,7 @@ // Конфигурация системы модерации const MODERATION_CONFIG = { // Задержка перед запуском модерации (в миллисекундах) - MODERATION_DELAY: 3000, // 3 секунды + MODERATION_DELAY: 1500, // 1.5 секунды // Включена ли система модерации MODERATION_ENABLED: true, @@ -13,6 +13,4 @@ const MODERATION_CONFIG = { ENABLE_MODERATION_LOGS: true }; -console.log(`⚙️ [Moderation Config] Конфигурация модерации загружена:`, MODERATION_CONFIG); - module.exports = MODERATION_CONFIG; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/get-constants.js b/server/routers/kfu-m-24-1/sber_mobile/get-constants.js index 0d7c355..47f34e8 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/get-constants.js +++ b/server/routers/kfu-m-24-1/sber_mobile/get-constants.js @@ -67,7 +67,8 @@ const getRagSupabaseUrl = async () => { module.exports = { getSupabaseUrl, getSupabaseKey, - getSupabaseServiceKey + getSupabaseServiceKey, + getGigaAuth }; // IIFE для установки переменных окружения diff --git a/server/routers/kfu-m-24-1/sber_mobile/index.js b/server/routers/kfu-m-24-1/sber_mobile/index.js index 5e393c8..653579f 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/index.js +++ b/server/routers/kfu-m-24-1/sber_mobile/index.js @@ -1,7 +1,5 @@ const router = require('express').Router(); -console.log(`🏗️ [Main Router] Загружаем основной роутер sber_mobile...`); -console.log(`🏗️ [Main Router] Время: ${new Date().toISOString()}`); const authRouter = require('./auth'); const { supabaseRouter } = require('./supabaseClient'); @@ -13,13 +11,9 @@ const chatsRouter = require('./chats'); const camerasRouter = require('./cameras'); const ticketsRouter = require('./tickets'); -console.log(`🏗️ [Main Router] Загружаем messagesRouter...`); const messagesRouter = require('./messages'); -console.log(`✅ [Main Router] messagesRouter загружен успешно`); -console.log(`🏗️ [Main Router] Загружаем moderationRouter...`); const moderationRouter = require('./moderation'); -console.log(`✅ [Main Router] moderationRouter загружен успешно`); const utilityPaymentsRouter = require('./utility_payments'); const apartmentsRouter = require('./apartments'); @@ -31,7 +25,6 @@ const supportRouter = require('./supportApi'); module.exports = router; -console.log(`🔗 [Main Router] Подключаем роутеры...`); router.use('/auth', authRouter); router.use('/supabase', supabaseRouter); @@ -43,13 +36,9 @@ router.use('', chatsRouter); router.use('', camerasRouter); router.use('', ticketsRouter); -console.log(`🔗 [Main Router] Подключаем messagesRouter...`); router.use('', messagesRouter); -console.log(`✅ [Main Router] messagesRouter подключен`); -console.log(`🔗 [Main Router] Подключаем moderationRouter...`); router.use('', moderationRouter); -console.log(`✅ [Main Router] moderationRouter подключен`); router.use('', utilityPaymentsRouter); router.use('', apartmentsRouter); @@ -58,7 +47,6 @@ router.use('', userApartmentsRouter); router.use('', avatarRouter); router.use('', supportRouter); -console.log(`🏗️ [Main Router] Все роутеры подключены успешно!`); diff --git a/server/routers/kfu-m-24-1/sber_mobile/messages.js b/server/routers/kfu-m-24-1/sber_mobile/messages.js index 25eb9fb..a495e33 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/messages.js +++ b/server/routers/kfu-m-24-1/sber_mobile/messages.js @@ -1,35 +1,13 @@ const router = require('express').Router(); const { getSupabaseClient } = require('./supabaseClient'); -const { getIo } = require('../../../io'); // Импортируем Socket.IO - -console.log(`📦 [Messages Module] Загружаем модуль messages.js...`); -console.log(`📦 [Messages Module] Время загрузки: ${new Date().toISOString()}`); - -try { - const { moderationText } = require('./chat-ai-agent/chat-moderation'); // Импортируем функцию модерации - console.log(`✅ [Messages Module] Функция модерации загружена успешно`); -} catch (error) { - console.error(`❌ [Messages Module] Ошибка загрузки функции модерации:`, error); -} - -try { - const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); // Импортируем конфигурацию модерации - console.log(`✅ [Messages Module] Конфигурация модерации загружена:`, MODERATION_CONFIG); -} catch (error) { - console.error(`❌ [Messages Module] Ошибка загрузки конфигурации модерации:`, error); -} - const { moderationText } = require('./chat-ai-agent/chat-moderation'); // Импортируем функцию модерации const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); // Импортируем конфигурацию модерации -console.log(`📦 [Messages Module] Модуль messages.js загружен полностью`); // Добавляем middleware для логирования всех запросов к messages роутеру // Тестовый эндпоинт для проверки работы роутера router.get('/messages/test', (req, res) => { - console.log(`🧪 [Messages Test] Тестовый эндпоинт вызван`); - console.log(`🧪 [Messages Test] Время: ${new Date().toISOString()}`); res.json({ status: 'OK', message: 'Messages router работает', @@ -38,53 +16,6 @@ router.get('/messages/test', (req, res) => { }); }); -// Тестовый эндпоинт для немедленной проверки AI агента -router.post('/messages/test-ai', async (req, res) => { - console.log(`🧪 [AI Test] === ТЕСТИРОВАНИЕ AI АГЕНТА ===`); - console.log(`🧪 [AI Test] Время: ${new Date().toISOString()}`); - - const { text } = req.body; - if (!text) { - return res.status(400).json({ error: 'text is required' }); - } - - console.log(`🧪 [AI Test] Тестируем текст: "${text}"`); - console.log(`🧪 [AI Test] Функция moderationText доступна: ${typeof moderationText}`); - - try { - console.log(`🧪 [AI Test] Вызываем AI агент напрямую...`); - const startTime = Date.now(); - const result = await moderationText('', text); - const endTime = Date.now(); - - console.log(`🧪 [AI Test] Результат от AI агента:`, result); - console.log(`🧪 [AI Test] Время выполнения: ${endTime - startTime}мс`); - - const [comment, isApproved, finalMessage] = result; - - res.json({ - success: true, - text: text, - comment: comment, - isApproved: isApproved, - finalMessage: finalMessage, - processingTime: endTime - startTime, - timestamp: new Date().toISOString() - }); - - } catch (error) { - console.error(`❌ [AI Test] Ошибка тестирования AI агента:`, error); - console.error(`❌ [AI Test] Stack:`, error.stack); - - res.status(500).json({ - success: false, - error: error.message, - stack: error.stack, - timestamp: new Date().toISOString() - }); - } -}); - // Получить все сообщения в чате с информацией о пользователе router.get('/messages', async (req, res) => { try { @@ -142,17 +73,10 @@ router.get('/messages', async (req, res) => { // Создать новое сообщение router.post('/messages', async (req, res) => { - console.log(`🚀 [Message Send] === ВХОД В POST /messages ЭНДПОИНТ ===`); - console.log(`🚀 [Message Send] Время входа: ${new Date().toISOString()}`); - console.log(`🚀 [Message Send] Request method: ${req.method}`); - console.log(`🚀 [Message Send] Request URL: ${req.originalUrl || req.url}`); - console.log(`🚀 [Message Send] Request body:`, JSON.stringify(req.body, null, 2)); - console.log(`🔌 [Message Send] Получаем Supabase клиент...`); let supabase; try { supabase = getSupabaseClient(); - console.log(`✅ [Message Send] Supabase клиент получен успешно`); } catch (error) { console.error(`❌ [Message Send] Ошибка получения Supabase клиента:`, error); return res.status(500).json({ error: 'Database connection error' }); @@ -160,11 +84,6 @@ router.post('/messages', async (req, res) => { const { chat_id, user_id, text } = req.body; - console.log(`📤 [Message Send] Получен запрос на отправку сообщения:`); - console.log(`📤 [Message Send] Chat ID: ${chat_id}`); - console.log(`📤 [Message Send] User ID: ${user_id}`); - console.log(`📤 [Message Send] Text length: ${text ? text.length : 0} символов`); - console.log(`📤 [Message Send] Text preview: "${text ? (text.length > 100 ? text.substring(0, 100) + '...' : text) : 'empty'}"`); if (!chat_id || !user_id || !text) { console.log(`❌ [Message Send] Отклонен: отсутствуют обязательные поля`); @@ -174,8 +93,6 @@ router.post('/messages', async (req, res) => { }); } - console.log(`💾 [Message Send] Сохраняем сообщение в Supabase...`); - // Создаем сообщение const { data: newMessage, error } = await supabase .from('messages') @@ -188,11 +105,6 @@ router.post('/messages', async (req, res) => { return res.status(400).json({ error: error.message }); } - console.log(`✅ [Message Send] Сообщение сохранено. ID: ${newMessage.id}`); - console.log(`📅 [Message Send] Время создания: ${newMessage.created_at}`); - - console.log(`👤 [Message Send] Получаем профиль пользователя ${user_id}...`); - // Получаем профиль пользователя const { data: userProfile, error: profileError } = await supabase .from('user_profiles') @@ -202,8 +114,6 @@ router.post('/messages', async (req, res) => { if (profileError) { console.log(`⚠️ [Message Send] Профиль пользователя не найден:`, profileError); - } else { - console.log(`✅ [Message Send] Профиль пользователя получен: ${userProfile.full_name || 'No name'}`); } // Объединяем сообщение с профилем @@ -211,168 +121,10 @@ router.post('/messages', async (req, res) => { ...newMessage, user_profiles: userProfile || null }; - - console.log(`📊 [Message Send] Итоговые данные сообщения подготовлены`); - - // === МОДЕРАЦИЯ ЧЕРЕЗ SUPABASE REAL-TIME === - console.log(`🔄 [Message Send] Модерация будет выполняться через Supabase Real-time подписку`); - console.log(`🔄 [Message Send] Статус модерации: ${MODERATION_CONFIG.MODERATION_ENABLED ? 'включена' : 'отключена'}`); - console.log(`🔄 [Message Send] Задержка модерации: ${MODERATION_CONFIG.MODERATION_DELAY}мс`); - console.log(`🔄 [Message Send] После создания сообщения в БД сработает Supabase Real-time подписка в polling-chat.js`); - - // Отправка через Socket.IO теперь происходит автоматически через Supabase Real-time подписку - // Это предотвращает дублирование сообщений - - console.log(`✅ [Message Send] Сообщение успешно отправлено. Возвращаем ответ клиенту`); - console.log(`📤 [Message Send] === Процесс отправки сообщения завершен ===`); res.json(data); }); -// Функция отложенной модерации сообщения -async function 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 || 'не указано'}`); - - // Отправляем обновление через Socket.IO всем клиентам в чате - console.log(`📡 [Moderation] Начинаем отправку обновления через Socket.IO...`); - try { - const io = getIo(); - if (io) { - console.log(`📡 [Moderation] Socket.IO подключение активно`); - - // Получаем профиль пользователя для полной информации - console.log(`📡 [Moderation] Получаем профиль пользователя для обновления...`); - const { data: userProfile, error: profileError } = await supabase - .from('user_profiles') - .select('id, full_name, avatar_url') - .eq('id', messageData.user_id) - .single(); - - if (profileError) { - console.log(`⚠️ [Moderation] Ошибка получения профиля пользователя:`, profileError); - } else { - console.log(`✅ [Moderation] Профиль пользователя получен: ${userProfile.full_name || 'No name'}`); - } - - const messageWithProfile = { - ...updatedMessage, - user_profiles: userProfile || null - }; - - console.log(`📡 [Moderation] Отправляем обновление в комнату chat_${messageData.chat_id}...`); - io.to(`chat_${messageData.chat_id}`).emit('message_updated', messageWithProfile); - console.log(`📤 [Moderation] ✅ Обновление сообщения отправлено в чат ${messageData.chat_id}`); - console.log(`📤 [Moderation] Событие: message_updated`); - console.log(`📤 [Moderation] Получатели: все участники чата ${messageData.chat_id}`); - } else { - console.log(`⚠️ [Moderation] Socket.IO подключение недоступно`); - } - } catch (socketError) { - console.error(`❌ [Moderation] Ошибка отправки через Socket.IO:`, socketError); - console.error(`❌ [Moderation] Детали ошибки Socket.IO:`, socketError.message); - } - } - } 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] Полная ошибка:`, error); - console.error(`❌ [Moderation] Стек ошибки:`, error.stack); - console.error(`❌ [Moderation] === КОНЕЦ ОБРАБОТКИ ОШИБКИ ===`); - } -} - // Получить конкретное сообщение router.get('/messages/:message_id', async (req, res) => { const supabase = getSupabaseClient(); diff --git a/server/routers/kfu-m-24-1/sber_mobile/moderation.js b/server/routers/kfu-m-24-1/sber_mobile/moderation.js index a3be652..7f54af9 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/moderation.js +++ b/server/routers/kfu-m-24-1/sber_mobile/moderation.js @@ -4,16 +4,11 @@ const { moderationText } = require('./chat-ai-agent/chat-moderation'); // Получить текущие настройки модерации router.get('/moderation/config', (req, res) => { - console.log(`⚙️ [Config] Запрос на получение конфигурации модерации`); - console.log(`⚙️ [Config] Текущие настройки:`, MODERATION_CONFIG); res.json(MODERATION_CONFIG); }); // Обновить настройки модерации router.post('/moderation/config', (req, res) => { - console.log(`⚙️ [Config] === ОБНОВЛЕНИЕ КОНФИГУРАЦИИ МОДЕРАЦИИ ===`); - console.log(`⚙️ [Config] Время запроса: ${new Date().toISOString()}`); - console.log(`⚙️ [Config] Полученные данные:`, req.body); const oldConfig = { ...MODERATION_CONFIG }; const { MODERATION_DELAY, MODERATION_ENABLED, BLOCKED_MESSAGE_TEXT, ENABLE_MODERATION_LOGS } = req.body; @@ -22,40 +17,30 @@ router.post('/moderation/config', (req, res) => { if (MODERATION_DELAY !== undefined) { const newValue = parseInt(MODERATION_DELAY); - console.log(`⚙️ [Config] Обновляем MODERATION_DELAY: ${MODERATION_CONFIG.MODERATION_DELAY} -> ${newValue}`); MODERATION_CONFIG.MODERATION_DELAY = newValue; changes.push(`MODERATION_DELAY: ${oldConfig.MODERATION_DELAY} -> ${newValue}`); } if (MODERATION_ENABLED !== undefined) { const newValue = Boolean(MODERATION_ENABLED); - console.log(`⚙️ [Config] Обновляем MODERATION_ENABLED: ${MODERATION_CONFIG.MODERATION_ENABLED} -> ${newValue}`); MODERATION_CONFIG.MODERATION_ENABLED = newValue; changes.push(`MODERATION_ENABLED: ${oldConfig.MODERATION_ENABLED} -> ${newValue}`); } if (BLOCKED_MESSAGE_TEXT !== undefined) { const newValue = String(BLOCKED_MESSAGE_TEXT); - console.log(`⚙️ [Config] Обновляем BLOCKED_MESSAGE_TEXT: "${MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT}" -> "${newValue}"`); MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT = newValue; changes.push(`BLOCKED_MESSAGE_TEXT: "${oldConfig.BLOCKED_MESSAGE_TEXT}" -> "${newValue}"`); } if (ENABLE_MODERATION_LOGS !== undefined) { - const newValue = Boolean(ENABLE_MODERATION_LOGS); - console.log(`⚙️ [Config] Обновляем ENABLE_MODERATION_LOGS: ${MODERATION_CONFIG.ENABLE_MODERATION_LOGS} -> ${newValue}`); + const newValue = Boolean(ENABLE_MODERATION_LOGS) MODERATION_CONFIG.ENABLE_MODERATION_LOGS = newValue; changes.push(`ENABLE_MODERATION_LOGS: ${oldConfig.ENABLE_MODERATION_LOGS} -> ${newValue}`); } - console.log(`⚙️ [Config] === ИТОГИ ОБНОВЛЕНИЯ ===`); - console.log(`⚙️ [Config] Количество изменений: ${changes.length}`); if (changes.length > 0) { changes.forEach((change, index) => { - console.log(`⚙️ [Config] ${index + 1}. ${change}`); }); } else { - console.log(`⚙️ [Config] Изменений не было внесено`); } - console.log(`⚙️ [Config] Новая конфигурация:`, MODERATION_CONFIG); - console.log(`⚙️ [Config] === ОБНОВЛЕНИЕ ЗАВЕРШЕНО ===`); res.json({ success: true, @@ -65,54 +50,4 @@ router.post('/moderation/config', (req, res) => { }); }); -// Тестовый эндпоинт для проверки модерации -router.post('/moderation/test', async (req, res) => { - const testStartTime = Date.now(); - - try { - const { text } = req.body; - - console.log(`🧪 [Moderation Test] === НАЧАЛО ТЕСТИРОВАНИЯ МОДЕРАЦИИ ===`); - console.log(`🧪 [Moderation Test] Время запуска: ${new Date().toISOString()}`); - console.log(`🧪 [Moderation Test] Текст для тестирования: "${text ? (text.length > 100 ? text.substring(0, 100) + '...' : text) : 'не указан'}"`); - console.log(`🧪 [Moderation Test] Длина текста: ${text ? text.length : 0} символов`); - - if (!text) { - console.log(`❌ [Moderation Test] Отклонен: текст не предоставлен`); - return res.status(400).json({ error: 'text is required' }); - } - - console.log(`🧪 [Moderation Test] Отправляем текст на модерацию...`); - const [comment, isApproved, finalMessage] = await moderationText('', text); - - const testTime = Date.now() - testStartTime; - console.log(`🧪 [Moderation Test] === РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ ===`); - console.log(`🧪 [Moderation Test] Время тестирования: ${testTime}мс`); - console.log(`🧪 [Moderation Test] Результат: ${isApproved ? '✅ ОДОБРЕНО' : '❌ ОТКЛОНЕНО'}`); - console.log(`🧪 [Moderation Test] Комментарий: "${comment || 'отсутствует'}"`); - console.log(`🧪 [Moderation Test] Финальное сообщение: "${finalMessage}"`); - console.log(`🧪 [Moderation Test] === ТЕСТИРОВАНИЕ ЗАВЕРШЕНО ===`); - - res.json({ - original_text: text, - is_approved: isApproved, - comment: comment, - final_message: finalMessage, - processing_time_ms: testTime - }); - } catch (error) { - const testTime = Date.now() - testStartTime; - console.error(`❌ [Moderation Test] === ОШИБКА ТЕСТИРОВАНИЯ ===`); - console.error(`❌ [Moderation Test] Время до ошибки: ${testTime}мс`); - console.error(`❌ [Moderation Test] Ошибка:`, error); - console.error(`❌ [Moderation Test] === КОНЕЦ ОБРАБОТКИ ОШИБКИ ===`); - - res.status(500).json({ - error: 'Moderation test failed', - details: error.message, - processing_time_ms: testTime - }); - } -}); - module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js b/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js index eb7c4df..d33ead2 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js +++ b/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js @@ -1,7 +1,12 @@ const { getSupabaseClient, initializationPromise } = require('./supabaseClient'); const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); +const { getGigaAuth } = require('./get-constants'); const { moderationText } = require('./chat-ai-agent/chat-moderation'); -const { getIo } = require('../../../io'); + +async function getGigaKey() { + const GIGA_AUTH = await getGigaAuth(); + return GIGA_AUTH; +} class ChatPollingHandler { constructor() { @@ -520,8 +525,8 @@ class ChatPollingHandler { // Очистка старых событий cleanupOldEvents() { const now = new Date(); - const MAX_EVENT_AGE = 24 * 60 * 60 * 1000; // 24 часа - const INACTIVE_USER_THRESHOLD = 60 * 60 * 1000; // 1 час + const MAX_EVENT_AGE = 1 * 60 * 60 * 1000; // 1 час + const INACTIVE_USER_THRESHOLD = 30 * 60 * 1000; // 30 минут // Очищаем старые события this.userEventQueues.forEach((eventQueue, user_id) => { @@ -618,13 +623,6 @@ 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') @@ -634,9 +632,7 @@ class ChatPollingHandler { if (profileError) { console.error('❌ [Supabase] Ошибка получения профиля пользователя:', profileError); - } else { - console.log(`✅ [Supabase Real-time] Профиль пользователя получен: ${userProfile.full_name || 'No name'}`); - } + } // Объединяем сообщение с профилем const messageWithProfile = { @@ -644,40 +640,26 @@ class ChatPollingHandler { user_profiles: userProfile || null }; - // Отправляем сообщение всем участникам чата - 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) { @@ -704,13 +686,6 @@ class ChatPollingHandler { 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') @@ -728,14 +703,11 @@ class ChatPollingHandler { 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); @@ -768,24 +740,15 @@ class ChatPollingHandler { 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; + const GIGA_AUTH = await getGigaKey(); + console.log(GIGA_AUTH) try { - const result = await moderationText('', messageText); - console.log(`🔍 [Moderation] Результат от AI агента получен:`, result); + const result = await moderationText('', messageText, GIGA_AUTH); [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); @@ -797,11 +760,6 @@ class ChatPollingHandler { } 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] Действие: сообщение остается без изменений`); @@ -842,19 +800,9 @@ class ChatPollingHandler { 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; diff --git a/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js b/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js index 0568afa..2ddeb2f 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js +++ b/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js @@ -6,10 +6,8 @@ let supabase = null; let initializationPromise = null; async function initSupabaseClient() { - console.log('🔄 [Supabase Client] Начинаем инициализацию...'); try { - console.log('🔄 [Supabase Client] Получаем конфигурацию...'); const supabaseUrl = await getSupabaseUrl(); const supabaseAnonKey = await getSupabaseKey(); const supabaseServiceRoleKey = await getSupabaseServiceKey(); @@ -49,7 +47,6 @@ router.post('/refresh-supabase-client', async (req, res) => { // GET /supabase-client-status router.get('/supabase-client-status', (req, res) => { - console.log('🔍 [Supabase Client] Проверяем статус клиента...'); const isInitialized = !!supabase;