(function() { const AGENT_ID = "692da0151980498cdf0f59c3"; const API_URL = "https://glav-bot.ru/api/functions"; // Persist Visitor ID let VISITOR_ID = localStorage.getItem('chat_visitor_id_' + AGENT_ID); if (!VISITOR_ID) { VISITOR_ID = 'visitor_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); localStorage.setItem('chat_visitor_id_' + AGENT_ID, VISITOR_ID); } const WIDGET_COLOR = "#0d0d0d"; const WELCOME_MESSAGE = "Привет! Как могу помочь?"; const AGENT_NAME = "Summator Support"; const SUGGESTIONS = []; console.log('ChatBot Init:', { AGENT_ID, API_URL }); // Debug log let isOpen = false; let messages = []; if (document.getElementById('chatbot-widget-root')) return; const root = document.createElement('div'); root.id = 'chatbot-widget-root'; document.body.appendChild(root); const style = document.createElement('style'); style.textContent = ` #chatbot-widget-root { font-family: system-ui, -apple-system, sans-serif; } #chatbot-widget-root * { box-sizing: border-box; } #chatbot-button { position: fixed; bottom: 20px; right: 20px; width: 60px; height: 60px; border-radius: 50%; background: ${WIDGET_COLOR}; border: none; cursor: pointer; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 2147483646; display: flex; align-items: center; justify-content: center; transition: transform 0.2s; } #chatbot-button:hover { transform: scale(1.05); } #chatbot-button svg { width: 28px; height: 28px; fill: white; } #chatbot-window { position: fixed; bottom: 90px; right: 20px; width: 380px; height: 600px; max-height: calc(100vh - 110px); background: white; border-radius: 16px; box-shadow: 0 8px 32px rgba(0,0,0,0.15); display: none; flex-direction: column; z-index: 2147483647; opacity: 0; transform: translateY(20px); transition: opacity 0.3s, transform 0.3s; } #chatbot-window.open { display: flex; opacity: 1; transform: translateY(0); } #chatbot-header { background: ${WIDGET_COLOR}; color: white; padding: 16px; border-radius: 16px 16px 0 0; font-weight: 600; display: flex; justify-content: space-between; align-items: center; } #chatbot-close { background: none; border: none; color: white; cursor: pointer; opacity: 0.8; padding: 4px; } #chatbot-close:hover { opacity: 1; } #chatbot-messages { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 12px; } .message { max-width: 85%; padding: 10px 14px; font-size: 14px; line-height: 1.5; } .message.user { align-self: flex-end; background: #e5e7eb; color: #1f2937; border-radius: 16px 16px 4px 16px; } .message.assistant { align-self: flex-start; background: #f3f4f6; color: #1f2937; border-radius: 16px 16px 16px 4px; } .message.assistant strong { font-weight: 600; } .message.assistant p { margin: 0; } .message.typing { color: #6b7280; font-size: 20px; padding: 4px 14px; line-height: 10px; } .message.typing .dot { animation: typing 1.4s infinite ease-in-out both; margin: 0 1px; } .message.typing .dot:nth-child(1) { animation-delay: -0.32s; } .message.typing .dot:nth-child(2) { animation-delay: -0.16s; } @keyframes typing { 0%, 80%, 100% { opacity: 0; } 40% { opacity: 1; } } #chatbot-input-area { border-top: 1px solid #e5e7eb; padding: 12px; display: flex; gap: 8px; background: white; border-radius: 0 0 16px 16px; } #chatbot-input { flex: 1; border: 1px solid #e5e7eb; border-radius: 8px; padding: 10px; outline: none; font-size: 14px; transition: border-color 0.2s; } #chatbot-input:focus { border-color: ${WIDGET_COLOR}; } #chatbot-send { background: ${WIDGET_COLOR}; color: white; border: none; padding: 0 12px; border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; } #chatbot-send:disabled { opacity: 0.5; cursor: not-allowed; } #chatbot-suggestions { display: flex; flex-wrap: wrap; gap: 8px; padding: 8px 16px 16px 16px; min-height: 40px; } .suggestion-btn { background: #f3f4f6; border: 1px solid #e5e7eb; color: #4b5563; padding: 8px 14px; border-radius: 16px; font-size: 13px; cursor: pointer; transition: all 0.2s; white-space: nowrap; flex-shrink: 0; box-shadow: 0 1px 2px rgba(0,0,0,0.05); } .suggestion-btn:hover { background: ${WIDGET_COLOR}; color: white; border-color: ${WIDGET_COLOR}; transform: translateY(-1px); } @media (max-width: 480px) { #chatbot-window { width: 100%; height: 100%; bottom: 0; right: 0; border-radius: 0; max-height: 100vh; } #chatbot-header { border-radius: 0; } #chatbot-button { bottom: 20px; right: 20px; } } `; document.head.appendChild(style); const button = document.createElement('button'); button.id = 'chatbot-button'; button.innerHTML = ''; button.onclick = toggleChat; root.appendChild(button); const chatWindow = document.createElement('div'); chatWindow.id = 'chatbot-window'; chatWindow.innerHTML = `
${AGENT_NAME}
`; root.appendChild(chatWindow); document.getElementById('chatbot-close').onclick = toggleChat; const input = document.getElementById('chatbot-input'); const sendBtn = document.getElementById('chatbot-send'); const messagesDiv = document.getElementById('chatbot-messages'); let lastMessageTimestamp = null; let pollInterval = null; let typingIndicator = null; function showTyping() { if (typingIndicator) return; typingIndicator = document.createElement('div'); typingIndicator.className = 'message assistant typing'; typingIndicator.innerHTML = '...'; messagesDiv.appendChild(typingIndicator); messagesDiv.scrollTop = messagesDiv.scrollHeight; } function hideTyping() { if (typingIndicator) { typingIndicator.remove(); typingIndicator = null; } } // Poll for new messages async function pollMessages() { if (!isOpen) return; try { const response = await fetch(API_URL + '/getWebsiteMessages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId: AGENT_ID, visitorId: VISITOR_ID, afterTimestamp: lastMessageTimestamp }) }); const data = await response.json(); // Handle Typing Indicator if (data.operatorTyping) { showTyping(); } else { hideTyping(); } if (data.messages && data.messages.length > 0) { data.messages.forEach(msg => { // Update timestamp to latest if (!lastMessageTimestamp || new Date(msg.timestamp) > new Date(lastMessageTimestamp)) { lastMessageTimestamp = msg.timestamp; } // Message Handling Logic const msgContent = msg.content; const msgRole = msg.role; // Hide system messages from widget if (msgRole === 'system') return; // Determine display role const displayRole = (msgRole === 'operator' || msgRole === 'assistant') ? 'assistant' : 'user'; // Check for duplicates in recent history // We compare against raw content if possible, but we only have display content in DOM/memory. // Note: We modify operator content with prefix. const isOperator = msgRole === 'operator'; const contentToCheck = isOperator ? '👮 ' + msgContent : msgContent; const isDuplicate = messages.slice(-10).some(m => m.content === contentToCheck && m.role === displayRole); if (!isDuplicate) { addMessage(displayRole, contentToCheck); } }); } } catch (e) { console.error('Poll error', e); } } function toggleChat() { isOpen = !isOpen; chatWindow.classList.toggle('open', isOpen); button.style.transform = isOpen ? 'scale(0)' : 'scale(1)'; if (isOpen) { // Initial load / Welcome if (messages.length === 0) { pollMessages().then(() => { const userMsgs = messages.filter(m => m.role === 'user'); // Show welcome message only if no messages at all if (messages.length === 0) { addMessage('assistant', WELCOME_MESSAGE); } // Always show suggestions (Persistent Menu) renderSuggestions(); }); // Force render immediately too, don't wait for poll renderSuggestions(); } else { // If we opened chat and already had messages, check if we should show suggestions renderSuggestions(); } if (!pollInterval) pollInterval = setInterval(pollMessages, 3000); setTimeout(() => input.focus(), 100); } else { clearInterval(pollInterval); pollInterval = null; } } function renderSuggestions(customSuggestions = null) { const container = document.getElementById('chatbot-suggestions'); if (!container) return; container.innerHTML = ''; const itemsToRender = customSuggestions || SUGGESTIONS; console.log('Rendering suggestions:', itemsToRender); // Debug if (itemsToRender && Array.isArray(itemsToRender) && itemsToRender.length > 0) { itemsToRender.forEach(item => { if (!item) return; let text = ''; let url = null; if (typeof item === 'string') { text = item; } else if (typeof item === 'object') { text = item.text; url = item.url; } if (!text) return; const btn = document.createElement('button'); btn.className = 'suggestion-btn'; btn.textContent = text; if (url) { // Link button btn.onclick = () => { window.open(url, '_blank'); }; // Add visual indicator for link btn.textContent += ' ↗'; } else { // Action button btn.onclick = () => { input.value = text; sendMessage(); // Do NOT clear suggestions - Persistent Menu }; } container.appendChild(btn); }); } } function addMessage(role, content) { messages.push({ role, content }); const msgDiv = document.createElement('div'); msgDiv.className = 'message ' + role; if (role === 'assistant') { let html = content .replace(/\*\*(.+?)\*\*/g, '$1') .replace(/\n/g, '
'); msgDiv.innerHTML = html; } else { msgDiv.textContent = content; } messagesDiv.appendChild(msgDiv); messagesDiv.scrollTop = messagesDiv.scrollHeight; // Clear suggestions on user message (if not already) // Removed clearing to keep menu persistent as requested /* if (role === 'user') { const container = document.getElementById('chatbot-suggestions'); if (container) container.innerHTML = ''; } */ } async function sendMessage() { const message = input.value.trim(); if (!message) return; const externalId = 'web_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); input.value = ''; input.disabled = true; sendBtn.disabled = true; addMessage('user', message); showTyping(); try { const response = await fetch(API_URL + '/sendWebsiteMessage', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId: AGENT_ID, visitorId: VISITOR_ID, message, externalId }) }); if (!response.ok) { hideTyping(); let errorDetails = response.status + ' ' + response.statusText; try { const errData = await response.json(); if (errData.error) errorDetails = errData.error; } catch (e) {} throw new Error(errorDetails); } const data = await response.json(); hideTyping(); if (data.response) { addMessage('assistant', data.response); } // Update suggestions if provided by API (dynamic or fallback) if (data.suggestions && Array.isArray(data.suggestions) && data.suggestions.length > 0) { console.log('Received dynamic suggestions:', data.suggestions); renderSuggestions(data.suggestions); } } catch (error) { hideTyping(); console.error('Chat error:', error); addMessage('assistant', 'Ошибка: ' + error.message + ' (' + API_URL + ')'); } finally { input.disabled = false; sendBtn.disabled = false; input.focus(); } } sendBtn.onclick = sendMessage; input.onkeypress = (e) => { if (e.key === 'Enter') sendMessage(); }; })();