add chat moderation
This commit is contained in:
@@ -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];
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
|
|
||||||
|
console.log(`🏗️ [Main Router] Загружаем основной роутер sber_mobile...`);
|
||||||
|
console.log(`🏗️ [Main Router] Время: ${new Date().toISOString()}`);
|
||||||
|
|
||||||
const authRouter = require('./auth');
|
const authRouter = require('./auth');
|
||||||
const { supabaseRouter } = require('./supabaseClient');
|
const { supabaseRouter } = require('./supabaseClient');
|
||||||
const profileRouter = require('./profile');
|
const profileRouter = require('./profile');
|
||||||
@@ -8,7 +12,15 @@ const additionalServicesRouter = require('./additional_services');
|
|||||||
const chatsRouter = require('./chats');
|
const chatsRouter = require('./chats');
|
||||||
const camerasRouter = require('./cameras');
|
const camerasRouter = require('./cameras');
|
||||||
const ticketsRouter = require('./tickets');
|
const ticketsRouter = require('./tickets');
|
||||||
|
|
||||||
|
console.log(`🏗️ [Main Router] Загружаем messagesRouter...`);
|
||||||
const messagesRouter = require('./messages');
|
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 utilityPaymentsRouter = require('./utility_payments');
|
||||||
const apartmentsRouter = require('./apartments');
|
const apartmentsRouter = require('./apartments');
|
||||||
const buildingsRouter = require('./buildings');
|
const buildingsRouter = require('./buildings');
|
||||||
@@ -19,6 +31,8 @@ const supportRouter = require('./supportApi');
|
|||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
|
console.log(`🔗 [Main Router] Подключаем роутеры...`);
|
||||||
|
|
||||||
router.use('/auth', authRouter);
|
router.use('/auth', authRouter);
|
||||||
router.use('/supabase', supabaseRouter);
|
router.use('/supabase', supabaseRouter);
|
||||||
router.use('', profileRouter);
|
router.use('', profileRouter);
|
||||||
@@ -28,7 +42,15 @@ router.use('', additionalServicesRouter);
|
|||||||
router.use('', chatsRouter);
|
router.use('', chatsRouter);
|
||||||
router.use('', camerasRouter);
|
router.use('', camerasRouter);
|
||||||
router.use('', ticketsRouter);
|
router.use('', ticketsRouter);
|
||||||
|
|
||||||
|
console.log(`🔗 [Main Router] Подключаем messagesRouter...`);
|
||||||
router.use('', 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('', utilityPaymentsRouter);
|
||||||
router.use('', apartmentsRouter);
|
router.use('', apartmentsRouter);
|
||||||
router.use('', buildingsRouter);
|
router.use('', buildingsRouter);
|
||||||
@@ -36,5 +58,7 @@ router.use('', userApartmentsRouter);
|
|||||||
router.use('', avatarRouter);
|
router.use('', avatarRouter);
|
||||||
router.use('', supportRouter);
|
router.use('', supportRouter);
|
||||||
|
|
||||||
|
console.log(`🏗️ [Main Router] Все роутеры подключены успешно!`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,89 @@ const router = require('express').Router();
|
|||||||
const { getSupabaseClient } = require('./supabaseClient');
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
const { getIo } = require('../../../io'); // Импортируем Socket.IO
|
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) => {
|
router.get('/messages', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@@ -59,15 +142,40 @@ router.get('/messages', async (req, res) => {
|
|||||||
|
|
||||||
// Создать новое сообщение
|
// Создать новое сообщение
|
||||||
router.post('/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;
|
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) {
|
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({
|
return res.status(400).json({
|
||||||
error: 'chat_id, user_id, and text are required'
|
error: 'chat_id, user_id, and text are required'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`💾 [Message Send] Сохраняем сообщение в Supabase...`);
|
||||||
|
|
||||||
// Создаем сообщение
|
// Создаем сообщение
|
||||||
const { data: newMessage, error } = await supabase
|
const { data: newMessage, error } = await supabase
|
||||||
.from('messages')
|
.from('messages')
|
||||||
@@ -75,27 +183,196 @@ router.post('/messages', async (req, res) => {
|
|||||||
.select('*')
|
.select('*')
|
||||||
.single();
|
.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')
|
.from('user_profiles')
|
||||||
.select('id, full_name, avatar_url')
|
.select('id, full_name, avatar_url')
|
||||||
.eq('id', user_id)
|
.eq('id', user_id)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
|
if (profileError) {
|
||||||
|
console.log(`⚠️ [Message Send] Профиль пользователя не найден:`, profileError);
|
||||||
|
} else {
|
||||||
|
console.log(`✅ [Message Send] Профиль пользователя получен: ${userProfile.full_name || 'No name'}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Объединяем сообщение с профилем
|
// Объединяем сообщение с профилем
|
||||||
const data = {
|
const data = {
|
||||||
...newMessage,
|
...newMessage,
|
||||||
user_profiles: userProfile || null
|
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 подписку
|
// Отправка через Socket.IO теперь происходит автоматически через Supabase Real-time подписку
|
||||||
// Это предотвращает дублирование сообщений
|
// Это предотвращает дублирование сообщений
|
||||||
|
|
||||||
|
console.log(`✅ [Message Send] Сообщение успешно отправлено. Возвращаем ответ клиенту`);
|
||||||
|
console.log(`📤 [Message Send] === Процесс отправки сообщения завершен ===`);
|
||||||
|
|
||||||
res.json(data);
|
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) => {
|
router.get('/messages/:message_id', async (req, res) => {
|
||||||
const supabase = getSupabaseClient();
|
const supabase = getSupabaseClient();
|
||||||
@@ -203,6 +480,4 @@ router.delete('/messages/:message_id', async (req, res) => {
|
|||||||
res.json({ success: true, message: 'Message deleted successfully' });
|
res.json({ success: true, message: 'Message deleted successfully' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
118
server/routers/kfu-m-24-1/sber_mobile/moderation.js
Normal file
118
server/routers/kfu-m-24-1/sber_mobile/moderation.js
Normal file
@@ -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;
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
const { getSupabaseClient, initializationPromise } = require('./supabaseClient');
|
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 {
|
class ChatPollingHandler {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -146,6 +149,16 @@ class ChatPollingHandler {
|
|||||||
const lastEventId = parseInt(last_event_id) || 0;
|
const lastEventId = parseInt(last_event_id) || 0;
|
||||||
const newEvents = eventQueue.filter(event => event.id > lastEventId);
|
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({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
events: newEvents,
|
events: newEvents,
|
||||||
@@ -605,6 +618,13 @@ class ChatPollingHandler {
|
|||||||
return;
|
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
|
const { data: userProfile, error: profileError } = await supabase
|
||||||
.from('user_profiles')
|
.from('user_profiles')
|
||||||
@@ -614,6 +634,8 @@ class ChatPollingHandler {
|
|||||||
|
|
||||||
if (profileError) {
|
if (profileError) {
|
||||||
console.error('❌ [Supabase] Ошибка получения профиля пользователя:', 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', {
|
this.broadcastToChat(newMessage.chat_id, 'new_message', {
|
||||||
message: messageWithProfile,
|
message: messageWithProfile,
|
||||||
timestamp: new Date()
|
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) {
|
} catch (callbackError) {
|
||||||
console.error('❌ [Supabase] Ошибка в обработчике сообщения:', 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() {
|
getConnectionStats() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user