const telegram = window.Telegram?.WebApp; const status = document.querySelector('#status'); const admin = document.querySelector('#admin'); const history = document.querySelector('#history'); const settings = document.querySelector('#settings'); const showHistory = document.querySelector('#show-history'); const showSettings = document.querySelector('#show-settings'); const clear = document.querySelector('#clear'); const form = document.querySelector('#form'); const turns = document.querySelector('#turns'); const spotifyStatus = document.querySelector('#spotify-status'); const spotifyConnect = document.querySelector('#spotify-connect'); const dateFormat = new Intl.DateTimeFormat('ru', { dateStyle: 'medium', timeStyle: 'short' }); async function api(path = '', options = {}) { const response = await fetch('/api/admin' + path, { ...options, headers: { Authorization: 'tma ' + telegram.initData, 'Content-Type': 'application/json' }, }); if (response.status === 401) throw new Error('Доступ открыт владельцу. Если сессия истекла, заново открой приложение из меню бота.'); if (response.status === 400) throw new Error('Проверь ID модели, голос и системный промпт.'); if (!response.ok) throw new Error('Сервер недоступен. Попробуй ещё раз.'); return response.json(); } function renderTurns(items) { turns.replaceChildren(); clear.disabled = items.length === 0; if (!items.length) { const empty = document.createElement('p'); empty.className = 'empty'; empty.textContent = 'Здесь появятся последние ответы станции.'; turns.append(empty); } for (const turn of items) { const article = document.createElement('article'); const date = document.createElement('time'); date.dateTime = turn.createdAt; date.textContent = dateFormat.format(new Date(turn.createdAt)); const question = document.createElement('p'); question.className = 'question'; question.textContent = turn.user; const answer = document.createElement('p'); answer.textContent = turn.assistant; article.append(date, question, answer); turns.append(article); } } function selectTab(isHistory) { showHistory.setAttribute('aria-pressed', String(isHistory)); showSettings.setAttribute('aria-pressed', String(!isHistory)); history.hidden = !isHistory; settings.hidden = isHistory; status.textContent = ''; } showHistory.onclick = async () => { selectTab(true); try { renderTurns((await api()).history); } catch (error) { status.textContent = error.message; } }; function renderSpotify(spotify) { spotifyStatus.textContent = !spotify.configured ? 'Spotify пока не настроен на сервере.' : spotify.connected ? 'Аккаунт подключён.' : 'Подключи аккаунт для управления музыкой.'; spotifyConnect.disabled = !spotify.configured; spotifyConnect.textContent = spotify.connected ? 'Подключить другой аккаунт' : 'Подключить Spotify'; } showSettings.onclick = async () => { selectTab(false); try { renderSpotify((await api()).spotify); } catch (error) { status.textContent = error.message; } }; spotifyConnect.onclick = async () => { spotifyConnect.disabled = true; try { const response = await fetch('/api/spotify/start', { method: 'POST', headers: { Authorization: 'tma ' + telegram.initData } }); if (!response.ok) throw new Error('Не удалось начать подключение Spotify.'); const { url } = await response.json(); telegram.openLink(url); } catch (error) { status.textContent = error.message; } finally { spotifyConnect.disabled = false; } }; form.onsubmit = async (event) => { event.preventDefault(); const button = form.querySelector('button'); button.disabled = true; try { const body = JSON.stringify(Object.fromEntries(new FormData(form))); await api('/settings', { method: 'PUT', body }); status.textContent = 'Сохранено. Настройки вступят в силу со следующего запроса.'; } catch (error) { status.textContent = error.message; } finally { button.disabled = false; } }; clear.onclick = () => { const erase = async (confirmed) => { if (!confirmed) return; clear.disabled = true; try { renderTurns((await api('/history', { method: 'DELETE' })).history); } catch (error) { status.textContent = error.message; clear.disabled = false; } }; if (telegram.isVersionAtLeast('6.2')) telegram.showConfirm('Очистить историю разговоров?', erase); else erase(window.confirm('Очистить историю разговоров?')); }; telegram?.ready(); if (!telegram?.initData) { status.textContent = 'Открой пульт из меню Telegram-бота.'; } else { try { const data = await api(); for (const [name, value] of Object.entries(data.settings)) form.elements.namedItem(name).value = value; renderTurns(data.history); renderSpotify(data.spotify); status.textContent = ''; admin.hidden = false; } catch (error) { status.textContent = error.message; } }