Spaces:
No application file
No application file
document.addEventListener("DOMContentLoaded", () => { | |
let mediaRecorder, audioChunks = [], audioStream, currentChatId = null; | |
const recordBtn = document.getElementById("record-btn"); | |
const stopBtn = document.getElementById("stop-btn"); | |
const sendBtn = document.getElementById("send-btn"); | |
const userInput = document.getElementById("user-input"); | |
const chatBox = document.getElementById("chat-box"); | |
const audioFileInput = document.getElementById("audio-file"); | |
const newChatBtn = document.getElementById("new-chat-btn"); | |
const chatList = document.getElementById("chat-list"); | |
const currentChatTitle = document.getElementById("current-chat-title"); | |
const fileInfo = document.getElementById("file-info"); | |
const fileName = document.getElementById("file-name"); | |
const clearFileBtn = document.getElementById("clear-file"); | |
// Emotion Map (скопирован из profile.html) | |
const emotionMap = { | |
'joy': '😊 Радость', | |
'neutral': '😐 Нейтрально', | |
'anger': '😠 Злость', | |
'sadness': '😢 Грусть', | |
'surprise': '😲 Удивление' | |
}; | |
// Инициализация при загрузке | |
initializeChats(); | |
function initializeChats() { | |
const savedChatId = localStorage.getItem('currentChatId'); | |
fetch("/get_chats") | |
.then(response => response.json()) | |
.then(chats => { | |
renderChatList(chats); | |
if (savedChatId && chats.some(c => c.chat_id === savedChatId)) { | |
loadChat(savedChatId); | |
} else if (chats.length > 0) { | |
loadChat(chats[0].chat_id); | |
} else { | |
showEmptyChatUI(); | |
} | |
}) | |
.catch(error => { | |
console.error("Ошибка загрузки чатов:", error); | |
showEmptyChatUI(); | |
}); | |
} | |
function showEmptyChatUI() { | |
if (chatBox) chatBox.innerHTML = '<div class="empty-chat">Нет активного чата</div>'; | |
} | |
function renderChatList(chats) { | |
if (!chatList) return; | |
chatList.innerHTML = ''; | |
chats.forEach(chat => { | |
const chatItem = document.createElement("div"); | |
chatItem.className = "chat-item"; | |
chatItem.dataset.chatId = chat.chat_id; | |
chatItem.innerHTML = ` | |
<div class="chat-item-main"> | |
<i class="fas fa-comment chat-icon"></i> | |
<div class="chat-item-content"> | |
<span class="chat-title">${chat.title}</span> | |
<span class="chat-date">${formatDate(chat.created_at)}</span> | |
</div> | |
</div> | |
<button class="delete-chat-btn" title="Удалить чат"> | |
<i class="fas fa-trash"></i> | |
</button> | |
`; | |
chatItem.querySelector('.chat-item-main').addEventListener('click', () => { | |
loadChat(chat.chat_id); | |
localStorage.setItem('currentChatId', chat.chat_id); | |
}); | |
chatItem.querySelector('.delete-chat-btn').addEventListener('click', (e) => { | |
e.stopPropagation(); | |
deleteChat(chat.chat_id); | |
}); | |
chatList.appendChild(chatItem); | |
}); | |
} | |
function formatDate(dateString) { | |
if (!dateString) return ''; | |
const date = new Date(dateString); | |
return date.toLocaleDateString('ru-RU'); | |
} | |
async function deleteChat(chatId) { | |
if (!confirm('Вы точно хотите удалить этот чат? Это действие нельзя отменить.')) return; | |
try { | |
const response = await fetch(`/delete_chat/${chatId}`, { | |
method: 'DELETE', | |
headers: { | |
'Content-Type': 'application/json', | |
'X-CSRFToken': getCSRFToken() | |
} | |
}); | |
const result = await response.json(); | |
if (result.success) { | |
if (currentChatId === chatId) { | |
chatBox.innerHTML = '<div class="empty-chat">Чат удалён</div>'; | |
currentChatId = null; | |
} | |
initializeChats(); // Перезагружаем список чатов | |
} else { | |
throw new Error(result.error || 'Ошибка при удалении чата'); | |
} | |
} catch (error) { | |
console.error('Delete chat error:', error); | |
appendMessage('bot', `❌ Ошибка при удалении: ${error.message}`); | |
} | |
} | |
function getCSRFToken() { | |
const meta = document.querySelector('meta[name="csrf-token"]'); | |
return meta ? meta.content : ''; | |
} | |
newChatBtn?.addEventListener("click", startNewChat); | |
function startNewChat() { | |
fetch("/start_chat", { | |
method: "POST", | |
headers: { "Content-Type": "application/json" }, | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
currentChatId = data.chat_id; | |
if (currentChatTitle) { | |
currentChatTitle.textContent = data.title; | |
} | |
chatBox.innerHTML = '<div class="message bot-message">Привет! Отправьте текст или голосовое сообщение для анализа эмоций.</div>'; | |
initializeChats(); | |
localStorage.setItem('currentChatId', data.chat_id); | |
}) | |
.catch(console.error); | |
} | |
function loadChat(chatId) { | |
fetch(`/load_chat/${chatId}`) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.error) throw new Error(data.error); | |
currentChatId = chatId; | |
currentChatTitle.textContent = data.title; | |
updateActiveChat(chatId); | |
chatBox.innerHTML = ""; | |
data.messages.forEach(msg => { | |
appendMessage(msg.sender, msg.content); | |
}); | |
localStorage.setItem('currentChatId', chatId); | |
}) | |
.catch(error => { | |
console.error("Ошибка загрузки чата:", error); | |
appendMessage("bot", `❌ Ошибка: ${error.message}`); | |
}); | |
} | |
function updateActiveChat(chatId) { | |
document.querySelectorAll(".chat-item").forEach(item => { | |
item.classList.toggle("active", item.dataset.chatId === chatId); | |
}); | |
} | |
sendBtn?.addEventListener("click", sendMessage); | |
userInput?.addEventListener("keypress", (e) => { | |
if (e.key === "Enter") sendMessage(); | |
}); | |
async function sendMessage() { | |
const text = userInput?.value.trim(); | |
if (!text || !currentChatId) return; | |
appendAndSaveMessage("user", text); | |
userInput.value = ""; | |
try { | |
const response = await fetch("/analyze", { | |
method: "POST", | |
headers: { "Content-Type": "application/json" }, | |
body: JSON.stringify({ text, chat_id: currentChatId }) | |
}); | |
const data = await response.json(); | |
appendAndSaveMessage("bot", `Эмоция: ${data.emotion} (${(data.confidence * 100).toFixed(1)}%)`); | |
} catch (error) { | |
console.error("Ошибка:", error); | |
appendAndSaveMessage("bot", `❌ Ошибка: ${error.message}`); | |
} | |
} | |
// Обработчики аудио | |
if (audioFileInput) { | |
audioFileInput.addEventListener("change", handleAudioUpload); | |
} | |
if (clearFileBtn) { | |
clearFileBtn.addEventListener("click", clearAudioFile); | |
} | |
function handleAudioUpload() { | |
const file = audioFileInput?.files[0]; | |
if (file) { | |
fileName.textContent = file.name; | |
fileInfo.style.display = 'flex'; | |
sendAudioFile(file); | |
} | |
} | |
function clearAudioFile() { | |
audioFileInput.value = ''; | |
fileInfo.style.display = 'none'; | |
} | |
async function sendAudioFile(file) { | |
if (!currentChatId) return; | |
appendAndSaveMessage("user", "Загружен аудиофайл..."); | |
try { | |
const formData = new FormData(); | |
formData.append("audio", file); | |
formData.append("chat_id", currentChatId); | |
const response = await fetch("/analyze_audio", { | |
method: "POST", | |
body: formData | |
}); | |
const data = await response.json(); | |
if (data.transcribed_text) { | |
appendAndSaveMessage("user", `Распознанный текст: ${data.transcribed_text}`); | |
} | |
appendAndSaveMessage("bot", `Эмоция: ${data.emotion} (${(data.confidence * 100).toFixed(1)}%)`); | |
clearAudioFile(); | |
} catch (error) { | |
console.error("Ошибка:", error); | |
appendAndSaveMessage("bot", `❌ Ошибка: ${error.message}`); | |
} | |
} | |
// Запись голоса | |
if (recordBtn) recordBtn.addEventListener("click", startRecording); | |
if (stopBtn) stopBtn.addEventListener("click", stopRecording); | |
async function startRecording() { | |
try { | |
audioStream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
mediaRecorder = new MediaRecorder(audioStream); | |
audioChunks = []; | |
mediaRecorder.ondataavailable = e => audioChunks.push(e.data); | |
mediaRecorder.onstop = async () => { | |
const audioBlob = new Blob(audioChunks, { type: "audio/wav" }); | |
sendAudioBlob(audioBlob); | |
}; | |
mediaRecorder.start(); | |
if (recordBtn) recordBtn.disabled = true; | |
if (stopBtn) stopBtn.disabled = false; | |
appendMessage("user", "Запись начата..."); | |
} catch (error) { | |
console.error("Ошибка записи:", error); | |
appendMessage("bot", "❌ Не удалось получить доступ к микрофону"); | |
} | |
} | |
function stopRecording() { | |
if (mediaRecorder?.state === "recording") { | |
mediaRecorder.stop(); | |
if (recordBtn) recordBtn.disabled = false; | |
if (stopBtn) stopBtn.disabled = true; | |
if (audioStream) audioStream.getTracks().forEach(track => track.stop()); | |
} | |
} | |
async function sendAudioBlob(audioBlob) { | |
if (!currentChatId) return; | |
appendAndSaveMessage("user", "Отправлено голосовое сообщение..."); | |
try { | |
const formData = new FormData(); | |
formData.append("audio", audioBlob, "recording.wav"); | |
formData.append("chat_id", currentChatId); | |
const response = await fetch("/analyze_audio", { | |
method: "POST", | |
body: formData | |
}); | |
const data = await response.json(); | |
if (data.transcribed_text) { | |
appendAndSaveMessage("user", `Распознанный текст: ${data.transcribed_text}`); | |
} | |
appendAndSaveMessage("bot", `Эмоция: ${data.emotion} (${(data.confidence * 100).toFixed(1)}%)`); | |
} catch (error) { | |
console.error("Ошибка:", error); | |
appendAndSaveMessage("bot", `❌ Ошибка: ${error.message}`); | |
} | |
} | |
function appendMessage(sender, text) { | |
const message = document.createElement("div"); | |
message.className = `message ${sender}-message`; | |
message.innerHTML = text; | |
if (chatBox) { | |
chatBox.appendChild(message); | |
chatBox.scrollTop = chatBox.scrollHeight; | |
} | |
} | |
function appendAndSaveMessage(sender, text) { | |
appendMessage(sender, text); | |
if (currentChatId) { | |
fetch("/save_message", { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json", | |
"X-CSRFToken": getCSRFToken() | |
}, | |
body: JSON.stringify({ | |
chat_id: currentChatId, | |
sender: sender, | |
content: text | |
}) | |
}).catch(console.error); | |
} | |
} | |
// Telegram анализ | |
document.getElementById('telegram-upload-form')?.addEventListener('submit', async function(e) { | |
e.preventDefault(); | |
const fileInput = document.getElementById('telegram-file'); | |
const file = fileInput.files[0]; | |
if (!file) { | |
alert('Пожалуйста, выберите файл'); | |
return; | |
} | |
try { | |
const formData = new FormData(); | |
formData.append('file', file); | |
const response = await fetch('/analyze_telegram_chat', { | |
method: 'POST', | |
body: formData | |
}); | |
const result = await response.json(); | |
if (result.error) { | |
throw new Error(result.error); | |
} | |
alert('Анализ завершен успешно!'); | |
updateTelegramAnalytics(); // Обновляем графики | |
} catch (error) { | |
console.error('Ошибка загрузки файла:', error); | |
alert(`Ошибка: ${error.message}`); | |
} | |
}); | |
// Функция скользящего среднего | |
function movingAverage(data, windowSize) { | |
const result = []; | |
for (let i = 0; i < data.length; i++) { | |
const start = Math.max(0, i - windowSize + 1); | |
const slice = data.slice(start, i + 1); | |
const avg = slice.reduce((sum, val) => sum + val, 0) / slice.length; | |
result.push(avg); | |
} | |
return result; | |
} | |
// Цвета эмоций | |
function getEmotionColor(emotion) { | |
const colors = { | |
'😊 Радость': '#00b894', | |
'😢 Грусть': '#0984e3', | |
'😠 Злость': '#d63031', | |
'😲 Удивление': '#fdcb6e', | |
'😨 Страх': '#a29bfe', | |
'😐 Нейтрально': '#636e72' | |
}; | |
return colors[emotion] || '#4a4ae8'; | |
} | |
// Иконки эмоций | |
function getEmotionIcon(emotion) { | |
const icons = { | |
'😊 Радость': 'fa-smile', | |
'😢 Грусть': 'fa-sad-tear', | |
'😠 Злость': 'fa-angry', | |
'😲 Удивление': 'fa-surprise', | |
'😨 Страх': 'fa-flushed', | |
'😐 Нейтрально': 'fa-meh' | |
}; | |
return icons[emotion] || 'fa-comment'; | |
} | |
// Основная функция анализа Telegram | |
// Основная функция анализа Telegram | |
async function updateTelegramAnalytics(range = 'month') { | |
try { | |
const response = await fetch('/get_telegram_analysis'); | |
const analyses = await response.json(); | |
if (!analyses || analyses.length === 0) { | |
document.getElementById('emotion-timeline').innerHTML = | |
'<div class="empty-state"><i class="fas fa-comment-slash"></i><p>Нет данных для отображения</p></div>'; | |
document.getElementById('emotion-distribution').innerHTML = ''; | |
return; | |
} | |
const allData = analyses.flatMap(a => JSON.parse(a.data)); | |
const emotionMap = { | |
'joy': '😊 Радость', | |
'sadness': '😢 Грусть', | |
'anger': '😠 Злость', | |
'surprise': '😲 Удивление', | |
'fear': '😨 Страх', | |
'no_emotion': '😐 Нейтрально' | |
}; | |
// Фильтрация по пользователю | |
const userSelect = document.getElementById('user-select'); | |
const selectedUser = userSelect?.value; | |
let filteredData = allData; | |
if (selectedUser && selectedUser !== 'all') { | |
filteredData = allData.filter(d => d.from === selectedUser); | |
} | |
const processedData = filteredData.map(d => ({ | |
emotion: emotionMap[d.emotion] || d.emotion, | |
from: d.from, | |
text: d.text, | |
date: new Date(d.timestamp), | |
confidence: d.confidence | |
})); | |
// --- Блок подготовки графиков --- | |
const groupByTime = (date, range) => { | |
const d = new Date(date); | |
if (range === 'week') { | |
d.setHours(0, 0, 0, 0); | |
d.setDate(d.getDate() - d.getDay()); | |
return d; | |
} else if (range === 'month') { | |
return new Date(d.getFullYear(), d.getMonth(), 1); | |
} else if (range === 'year') { | |
return new Date(d.getFullYear(), 0, 1); | |
} | |
return new Date(d.getFullYear(), d.getMonth(), d.getDate()); | |
}; | |
const groupedData = {}; | |
processedData.forEach(d => { | |
const timeKey = groupByTime(d.date, range).getTime(); | |
if (!groupedData[timeKey]) { | |
groupedData[timeKey] = { | |
date: new Date(timeKey), | |
emotions: {} | |
}; | |
} | |
if (!groupedData[timeKey].emotions[d.emotion]) { | |
groupedData[timeKey].emotions[d.emotion] = { | |
count: 0, | |
totalConfidence: 0 | |
}; | |
} | |
groupedData[timeKey].emotions[d.emotion].count++; | |
groupedData[timeKey].emotions[d.emotion].totalConfidence += d.confidence; | |
}); | |
const timeKeys = Object.keys(groupedData).sort(); | |
const emotions = [...new Set(processedData.map(d => d.emotion))]; | |
const traces = emotions.map(emotion => { | |
const x = []; | |
const y = []; | |
const customdata = []; | |
timeKeys.forEach(key => { | |
const dataPoint = groupedData[key]; | |
if (dataPoint.emotions[emotion]) { | |
x.push(dataPoint.date); | |
y.push(dataPoint.emotions[emotion].count); | |
customdata.push({ | |
emotion: emotion, | |
avgConfidence: (dataPoint.emotions[emotion].totalConfidence / | |
dataPoint.emotions[emotion].count).toFixed(2) | |
}); | |
} else { | |
x.push(dataPoint.date); | |
y.push(0); | |
customdata.push(null); | |
} | |
}); | |
return { | |
x: x, | |
y: y, | |
name: emotion, | |
type: 'scatter', | |
mode: 'lines+markers', | |
line: { shape: 'spline' }, | |
marker: { color: getEmotionColor(emotion), size: 6 }, | |
fill: 'tonexty', | |
fillcolor: `${getEmotionColor(emotion)}7F`, | |
customdata: customdata, | |
hovertemplate: | |
'<b>%{x|%d %b %Y}</b><br>' + | |
'Эмоция: %{fullData.name}<br>' + | |
'Сообщений: %{y}<br>' + | |
'Средняя уверенность: %{customdata.avgConfidence}<extra></extra>' | |
}; | |
}); | |
Plotly.newPlot('emotion-timeline', traces, { | |
title: false, | |
plot_bgcolor: 'rgba(0,0,0,0)', | |
paper_bgcolor: 'rgba(0,0,0,0)', | |
font: { color: 'white' }, | |
xaxis: { | |
title: 'Дата', | |
tickformat: range === 'year' ? '%Y' : range === 'month' ? '%b %Y' : '%d %b', | |
gridcolor: 'rgba(255,255,255,0.1)' | |
}, | |
yaxis: { | |
title: 'Количество сообщений', | |
gridcolor: 'rgba(255,255,255,0.1)' | |
}, | |
hovermode: 'closest', | |
legend: { | |
orientation: 'h', | |
y: -0.2 | |
}, | |
margin: { t: 0, b: 80 } | |
}); | |
// Тепловая карта | |
const dates = [...new Set(processedData.map(d => d.date.toDateString()))]; | |
const emotionLabels = Object.values(emotionMap); | |
const z = emotionLabels.map(e => dates.map(d => | |
processedData.filter(msg => msg.date.toDateString() === d && msg.emotion === e).length | |
)); | |
Plotly.newPlot('calendar-heatmap', [{ | |
type: 'heatmap', | |
z: z, | |
x: dates, | |
y: emotionLabels, | |
colorscale: [ | |
[0, '#2d3436'], // Темный фон | |
[0.5, '#6c5ce7'], | |
[1, '#00b894'] | |
], | |
showscale: true, | |
colorbar: { | |
title: 'Частота', | |
titleside: 'top', | |
tickmode: 'array', | |
tickvals: [0, Math.max(...z.flat())], | |
ticktext: ['Мало', 'Много'], | |
ticks: 'outside' | |
} | |
}], { | |
title: 'Тепловая карта эмоций по дням', | |
xaxis: { | |
title: 'Дата', | |
tickangle: -45 | |
}, | |
yaxis: { | |
title: 'Эмоции', | |
automargin: true | |
}, | |
margin: { t: 30, r: 30, l: 80, b: 80 } | |
}); | |
// Автоматический анализ | |
const totalMessages = processedData.length; | |
const emotionCounts = {}; | |
processedData.forEach(d => { | |
emotionCounts[d.emotion] = (emotionCounts[d.emotion] || 0) + 1; | |
}); | |
const sorted = Object.entries(emotionCounts).sort((a, b) => b[1] - a[1]); | |
const dominant = sorted[0]; | |
const sadnessPeaks = processedData | |
.filter(d => d.emotion === '😢 Грусть') | |
.reduce((acc, d) => { | |
const key = d.date.toDateString(); | |
acc[key] = (acc[key] || 0) + 1; | |
return acc; | |
}, {}); | |
const sadPeak = Object.entries(sadnessPeaks).sort((a, b) => b[1] - a[1])[0]; | |
document.getElementById('summary-content').innerHTML = ` | |
<ul style="color: white;"> | |
<li>💡 Преобладает: ${dominant[0]} (${((dominant[1]/totalMessages)*100).toFixed(1)}%)</li> | |
<li>📉 Пик грусти: ${sadPeak[0]} (${sadPeak[1]} сообщений)</li> | |
</ul> | |
`; | |
// Круговая диаграмма и статистика | |
const pieLabels = Object.keys(emotionCounts); | |
const pieValues = Object.values(emotionCounts); | |
Plotly.newPlot('emotion-distribution-pie', [{ | |
labels: pieLabels, | |
values: pieValues, | |
type: 'pie', | |
textinfo: 'label+percent', | |
hoverinfo: 'label+value+percent', | |
marker: { | |
colors: pieLabels.map(e => getEmotionColor(e)) | |
}, | |
textfont: { | |
color: 'white' | |
}, | |
hole: 0.4, | |
rotation: 45 | |
}], { | |
title: false, | |
plot_bgcolor: 'rgba(0,0,0,0)', | |
paper_bgcolor: 'rgba(0,0,0,0)', | |
font: { color: 'white' }, | |
showlegend: false, | |
margin: { t: 0, b: 0, l: 0, r: 0 } | |
}); | |
// Статистика по эмоциям — 2 строки по 3 эмоции | |
const statsHTML = pieLabels.slice(0, 6).map((emotion, i) => { | |
const percentage = ((pieValues[i] / totalMessages) * 100).toFixed(1); | |
return ` | |
<div class="emotion-stat"> | |
<div class="emotion-label" style="color: ${getEmotionColor(emotion)}"> | |
<span>${emotion.replace(/[\u{1F600}-\u{1F64F}]/gu, '')}</span> | |
</div> | |
<div class="confidence-bar"> | |
<div class="confidence-fill" | |
style="width: ${percentage}%; | |
background: ${getEmotionColor(emotion)};"></div> | |
</div> | |
<div class="confidence-value">${percentage}%</div> | |
</div>`; | |
}).join(''); | |
document.getElementById('emotion-distribution').innerHTML = statsHTML; | |
// Адаптация сетки на фронтенде | |
const statsContainer = document.getElementById('emotion-distribution'); | |
if (statsContainer) { | |
statsContainer.style.display = 'grid'; | |
statsContainer.style.gridTemplateColumns = 'repeat(3, 1fr)'; | |
statsContainer.style.gap = '15px'; | |
} | |
// --- Выбор пользователя --- | |
populateUserSelect(processedData); | |
} catch (error) { | |
console.error('Ошибка обновления аналитики:', error); | |
document.getElementById('emotion-timeline').innerHTML = | |
`<div class="empty-state"><i class="fas fa-exclamation-triangle"></i><p>Ошибка загрузки данных: ${error.message}</p></div>`; | |
} | |
} | |
// Глобальная переменная для хранения списка пользователей | |
let telegramUsers = []; | |
function populateUserSelect(processedData) { | |
const userSelect = document.getElementById('user-select'); | |
if (!userSelect) return; | |
const users = [...new Set(processedData.map(d => d.from))]; | |
// Если пользователи не изменились — ничего не делаем | |
if (JSON.stringify(users.sort()) === JSON.stringify(telegramUsers.sort())) return; | |
telegramUsers = users; | |
userSelect.innerHTML = '<option value="all">Все участники</option>'; | |
users.forEach(user => { | |
const option = document.createElement('option'); | |
option.value = user; | |
option.textContent = user; | |
userSelect.appendChild(option); | |
}); | |
// Добавляем обработчик только один раз | |
if (!userSelect.dataset.listenerAdded) { | |
userSelect.addEventListener('change', () => { | |
updateTelegramAnalytics(document.querySelector('.time-btn.active')?.dataset.range || 'month'); | |
}); | |
userSelect.dataset.listenerAdded = 'true'; | |
} | |
} | |
document.querySelectorAll('.time-btn').forEach(button => { | |
button.addEventListener('click', function () { | |
// Удаляем класс 'active' у всех кнопок | |
document.querySelectorAll('.time-btn').forEach(btn => btn.classList.remove('active')); | |
// Добавляем класс 'active' текущей кнопке | |
this.classList.add('active'); | |
// Получаем диапазон времени из атрибута data-range | |
const range = this.getAttribute('data-range'); | |
// Вызываем функцию обновления графиков с новым диапазоном | |
updateTelegramAnalytics(range); | |
}); | |
}); | |
// Проверяем, есть ли сохранённый активный временной интервал в localStorage или просто ставим 'month' | |
window.addEventListener('load', () => { | |
const activeTimeBtn = document.querySelector('.time-btn.active'); | |
if (activeTimeBtn) { | |
const range = activeTimeBtn.dataset.range || 'month'; | |
updateTelegramAnalytics(range); | |
} else { | |
updateTelegramAnalytics('month'); | |
} | |
}); | |
// Инициализация при загрузке страницы | |
if (window.location.pathname.includes('/profile')) { | |
updateTelegramAnalytics(); | |
} | |
}); |