(function() { let sessionId = getCookie('chatSessionId'); if (!sessionId) { sessionId = 'chat_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); setCookie('chatSessionId', sessionId, 365); } let pollingInterval = null; let isPolling = false; let lastProcessedMessageId = 0; let historyLoaded = false; let lastMessageTime = Date.now(); // НОВОЕ: отслеживание времени последнего сообщения let inactivityTimeout = null; // НОВОЕ: таймер неактивности let lastResponseTime = null; // НОВОЕ: время последнего ответа от ассистента/админа function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); return null; } function setCookie(name, value, days) { const expires = new Date(); expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000)); document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/`; } function initChatUI() { const style = document.createElement('style'); style.textContent = ` .floating-chat-btn { position: fixed; bottom: 30px; left: 30px; width: 70px; height: 70px; border-radius: 50%; background: linear-gradient(135deg, #43a8b9 0%, #3095dd 100%); display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 4px 12px rgba(67, 168, 185, 0.3); z-index: 9999; transition: all 0.3s ease; border: none; font-size: 32px; } .floating-chat-btn:hover { transform: scale(1.1); box-shadow: 0 6px 20px rgba(67, 168, 185, 0.5); } .chat-window { position: fixed; bottom: 110px; left: 30px; width: 420px; height: 600px; border-radius: 12px; background: white; box-shadow: 0 5px 40px rgba(0,0,0,0.16); z-index: 9998; display: none; flex-direction: column; overflow: hidden; } .chat-window.active { display: flex; animation: slideUp 0.3s ease; } @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .chat-header { background: linear-gradient(135deg, #43a8b9 0%, #3095dd 100%); color: white; padding: 20px; font-weight: bold; display: flex; justify-content: space-between; align-items: center; } .chat-close { background: none; border: none; color: white; font-size: 20px; cursor: pointer; padding: 0; } .chat-messages { flex: 1; overflow-y: auto; padding: 20px; background: #f9f9f9; } .message { margin-bottom: 15px; display: flex; gap: 12px; align-items: flex-end; } .message.user { justify-content: flex-end; } .message-icon { width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0; } .message.assistant .message-icon { background: #e0e0e0; color: #333; } .message.admin .message-icon { background: #fff3cd; color: #333; } .message.user .message-icon { display: none; } .message-text { max-width: 70%; padding: 12px 16px; border-radius: 12px; word-wrap: break-word; line-height: 1.4; } .message.user .message-text { background: linear-gradient(135deg, #43a8b9 0%, #3095dd 100%); color: white; border-bottom-right-radius: 4px; } .message.assistant .message-text { background: #e0e0e0; color: #333; border-bottom-left-radius: 4px; } .message.admin .message-text { background: #fff3cd; color: #333; border-bottom-left-radius: 4px; border-left: 3px solid #ffc107; } .message.typing .message-text { background: #e0e0e0; color: #333; border-bottom-left-radius: 4px; padding: 12px 16px; } .message.typing .message-icon { background: #e0e0e0; } .chat-input-area { padding: 15px; border-top: 1px solid #ddd; display: flex; gap: 10px; } .chat-input { flex: 1; border: 1px solid #ddd; border-radius: 6px; padding: 10px 12px; font-size: 14px; } .chat-send { background: linear-gradient(135deg, #43a8b9 0%, #3095dd 100%); color: white; border: none; border-radius: 6px; padding: 10px 16px; cursor: pointer; font-weight: bold; } .chat-send:disabled { opacity: 0.5; cursor: not-allowed; } .chat-send:hover:not(:disabled) { opacity: 0.9; } .chat-closed-notice { padding: 15px; background: #f8d7da; color: #721c24; border-top: 1px solid #ddd; text-align: center; font-size: 12px; display: none; } .chat-closed-notice.show { display: block; } .inactivity-notice { padding: 15px; background: #cfe2ff; color: #084298; border-top: 1px solid #ddd; text-align: center; font-size: 12px; display: none; } .inactivity-notice.show { display: block; } .typing-indicator { display: flex; gap: 4px; align-items: center; height: 16px; } .typing-indicator span { width: 8px; height: 8px; border-radius: 50%; background: #999; animation: typing 1.4s infinite; } .typing-indicator span:nth-child(2) { animation-delay: 0.2s; } .typing-indicator span:nth-child(3) { animation-delay: 0.4s; } @keyframes typing { 0%, 60%, 100% { opacity: 0.5; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-8px); } } @media (max-width: 480px) { .floating-chat-btn { width: 60px; height: 60px; bottom: 20px; left: 20px; } .chat-window { bottom: 90px; left: 10px; right: 10px; width: auto; height: 500px; } } `; document.head.appendChild(style); const btn = document.createElement('button'); btn.className = 'floating-chat-btn'; btn.innerHTML = '🤖'; document.body.appendChild(btn); const chatWindow = document.createElement('div'); chatWindow.className = 'chat-window'; chatWindow.innerHTML = `
Чат ассистент
Разговор завершён
Сессия закрыта из-за неактивности
`; document.body.appendChild(chatWindow); btn.addEventListener('click', function() { chatWindow.classList.toggle('active'); if (chatWindow.classList.contains('active')) { if (!historyLoaded) { loadChatHistory(); } document.getElementById('chatInput').focus(); } }); document.getElementById('closeBtn').addEventListener('click', function() { chatWindow.classList.remove('active'); }); const sendBtn = document.getElementById('sendBtn'); const inputField = document.getElementById('chatInput'); sendBtn.addEventListener('click', sendMessage); inputField.addEventListener('keypress', function(e) { if (e.key === 'Enter' && !inputField.disabled) sendMessage(); }); // НОВОЕ: отслеживание активности inputField.addEventListener('input', function() { lastMessageTime = Date.now(); resetInactivityTimer(); }); function loadChatHistory() { console.log('Loading chat history for sessionId:', sessionId); fetch('https://mafa.ccpr.ru/webhook/490e19ae-74ca-4a17-a4a7-c10b2b859a70', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: sessionId, action: 'poll', timestamp: new Date().toISOString() }) }) .then(r => r.json()) .then(data => { console.log('History response:', data); processMessages(data, true); historyLoaded = true; if (!isPolling) { startPolling(); } }) .catch(e => { console.error('History load error:', e); historyLoaded = true; }); } function sendMessage() { const messageText = inputField.value.trim(); if (!messageText) return; lastMessageTime = Date.now(); // НОВОЕ: обновляем время resetInactivityTimer(); // НОВОЕ: сбрасываем таймер неактивности addMessageToUI(messageText, 'user'); inputField.value = ''; showTypingIndicator(); fetch('https://mafa.ccpr.ru/webhook/490e19ae-74ca-4a17-a4a7-c10b2b859a70', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: sessionId, action: 'message', message: messageText, timestamp: new Date().toISOString() }) }) .then(r => r.json()) .then(data => { console.log('Send response:', data); if (!isPolling) { startPolling(); } }) .catch(e => { console.error('Send error:', e); removeTypingIndicator(); }); } function addMessageToUI(text, sender) { const messagesDiv = document.getElementById('chatMessages'); const messageEl = document.createElement('div'); messageEl.className = `message ${sender}`; let icon = ''; if (sender === 'assistant') { icon = '
🤖
'; } else if (sender === 'admin') { icon = '
👤
'; } messageEl.innerHTML = `${icon}
${escapeHtml(text)}
`; messagesDiv.appendChild(messageEl); messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; let html = div.innerHTML; // Преобразуем ссылки в активные, игнорируя скобки в конце html = html.replace(/(https?:\/\/[^\s\)\]\}]+)/g, function(match) { // Удаляем закрывающие скобки если они в конце let url = match.replace(/[\)\]\}]+$/, ''); return '' + url + ''; }); return html; } function processMessages(data, isHistory = false) { console.log('processMessages data:', data, 'isHistory:', isHistory); let messages = []; if (data && data.data && Array.isArray(data.data)) { messages = data.data; } else if (Array.isArray(data) && data[0] && data[0].data && Array.isArray(data[0].data)) { messages = data[0].data; } console.log('Extracted messages:', messages); if (messages.length === 0) { console.warn('No messages found in response'); return; } messages.forEach(msg => { if (msg.id <= lastProcessedMessageId && !isHistory) { console.log('Skipping already processed message id:', msg.id); return; } let senderType = 'assistant'; if (msg.message_type === 'user') { senderType = 'user'; } else if (msg.message_type === 'admin') { senderType = 'admin'; } else if (msg.message_type === 'assistant') { senderType = 'assistant'; } console.log('Adding message id:', msg.id, 'text:', msg.message_text, 'type:', senderType); addMessageToUI(msg.message_text, senderType); lastProcessedMessageId = msg.id; }); if (!isHistory && messages.length > 0) { const lastMessage = messages[messages.length - 1]; if (lastMessage.status === false) { stopPolling(); showChatClosed(); } } } function startPolling() { isPolling = true; console.log('Starting polling with lastProcessedMessageId:', lastProcessedMessageId); pollingInterval = setInterval(function() { // НОВОЕ: проверяем неактивность (3 минуты = 180000 мс) const timeSinceLastMessage = Date.now() - lastMessageTime; if (timeSinceLastMessage > 180000) { // 3 минуты console.log('User inactive for 3 minutes, stopping polling'); stopPolling(); showInactivityNotice(); return; } // НОВОЕ: проверяем таймаут ответа (5 минут = 300000 мс) if (lastResponseTime !== null) { const timeSinceLastResponse = Date.now() - lastResponseTime; if (timeSinceLastResponse > 300000) { // 5 минут console.log('No response for 5 minutes, stopping polling'); stopPolling(); showInactivityNotice(); return; } } fetch('https://mafa.ccpr.ru/webhook/490e19ae-74ca-4a17-a4a7-c10b2b859a70', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: sessionId, action: 'poll', timestamp: new Date().toISOString() }) }) .then(r => r.json()) .then(data => { console.log('Poll response:', data); let messages = []; if (data && data.data && Array.isArray(data.data)) { messages = data.data; } else if (Array.isArray(data) && data[0] && data[0].data && Array.isArray(data[0].data)) { messages = data[0].data; } if (messages.length > 0) { const newResponses = messages.filter(msg => msg.id > lastProcessedMessageId && (msg.message_type === 'assistant' || msg.message_type === 'admin') ); console.log('New responses found:', newResponses.length); if (newResponses.length > 0) { removeTypingIndicator(); lastMessageTime = Date.now(); // Обновляем время последнего сообщения (важно!) lastResponseTime = Date.now(); // НОВОЕ: обновляем время ответа newResponses.forEach(msg => { let senderType = msg.message_type === 'admin' ? 'admin' : 'assistant'; console.log('Adding new response:', msg.message_text, 'type:', senderType); addMessageToUI(msg.message_text, senderType); lastProcessedMessageId = msg.id; }); } const maxId = Math.max(...messages.map(m => m.id)); if (maxId > lastProcessedMessageId) { lastProcessedMessageId = maxId; } const lastMessage = messages[messages.length - 1]; if (lastMessage.status === false) { stopPolling(); showChatClosed(); } } }) .catch(e => { console.error('Poll error:', e); stopPolling(); }); }, 4000); } function stopPolling() { if (pollingInterval) { clearInterval(pollingInterval); pollingInterval = null; } isPolling = false; removeTypingIndicator(); } // НОВОЕ: сброс таймера неактивности function resetInactivityTimer() { if (inactivityTimeout) { clearTimeout(inactivityTimeout); } // Опционально: можно добавить таймер для закрытия чата через 5 минут } function showTypingIndicator() { removeTypingIndicator(); const messagesDiv = document.getElementById('chatMessages'); const typingEl = document.createElement('div'); typingEl.id = 'typingIndicator'; typingEl.className = 'message typing'; typingEl.innerHTML = '
🤖
'; messagesDiv.appendChild(typingEl); messagesDiv.scrollTop = messagesDiv.scrollHeight; } function removeTypingIndicator() { const typingEl = document.getElementById('typingIndicator'); if (typingEl) typingEl.remove(); } function showChatClosed() { const notice = document.getElementById('chatClosedNotice'); notice.classList.add('show'); } // НОВОЕ: показываем уведомление о неактивности function showInactivityNotice() { const notice = document.getElementById('inactivityNotice'); notice.classList.add('show'); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initChatUI); } else { initChatUI(); } })();