feat(assistant): срок жизни диалога — 7 дней без общения

saveChat пишет метку времени, loadChat сбрасывает диалог, если с последней
реплики прошло больше CHAT_TTL (7 дней). Обратная совместимость со старым
форматом-массивом. Сноска обновлена.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-24 23:07:11 +03:00
parent 7c0ccc7282
commit e38abff02a
+16 -4
View File
@@ -546,10 +546,22 @@
var SUGGESTIONS = ['Как вырезать кусок учебника?', 'Как создать карточки?', 'Как начать тест?', 'Объясни теорему Пифагора', 'Где мои домашние задания?', 'Как включить тёмную тему?'];
var TEACHER_SUGGESTIONS = ['Как создать класс и выдать задание?', 'Идеи заданий по теме…', 'Составь план урока по теме…', 'Как работает журнал и аналитика?', 'Как провести онлайн-урок?'];
var _chat = []; // многоходовой диалог: [{role:'user'|'assistant', content}]
// Диалог переживает обновление/переходы (localStorage, per-user), пока ученик сам не нажмёт «Очистить».
// Диалог переживает обновление/переходы (localStorage, per-user), пока ученик сам не нажмёт «Очистить»
// или пока не пройдёт CHAT_TTL без новых сообщений (срок жизни от последней реплики).
var CHAT_TTL = 7 * 24 * 3600 * 1000; // 7 дней
function _chatKey() { try { var u = LS.getUser && LS.getUser(); return u && u.id ? 'asst_chat_' + u.id : null; } catch (e) { return null; } }
function saveChat() { var k = _chatKey(); if (k) lsSet(k, JSON.stringify(_chat.slice(-30))); }
function loadChat() { var k = _chatKey(); if (!k) return; try { var a = JSON.parse(lsGet(k) || '[]'); if (Array.isArray(a)) _chat = a.filter(function (m) { return m && (m.role === 'user' || m.role === 'assistant') && typeof m.content === 'string'; }); } catch (e) {} }
function saveChat() { var k = _chatKey(); if (k) lsSet(k, JSON.stringify({ t: Date.now(), c: _chat.slice(-30) })); }
function loadChat() {
var k = _chatKey(); if (!k) return;
try {
var raw = JSON.parse(lsGet(k) || 'null'), arr, ts = null;
if (Array.isArray(raw)) arr = raw; // старый формат (без метки времени)
else if (raw && Array.isArray(raw.c)) { arr = raw.c; ts = raw.t; }
else return;
if (ts && (Date.now() - ts) > CHAT_TTL) { clearChatStore(); return; } // протух — забываем
_chat = arr.filter(function (m) { return m && (m.role === 'user' || m.role === 'assistant') && typeof m.content === 'string'; });
} catch (e) {}
}
function clearChatStore() { var k = _chatKey(); if (k) try { localStorage.removeItem(k); } catch (e) {} }
function msgEl(role) { var d = document.createElement('div'); d.className = 'asst-msg asst-msg-' + role; return d; }
function renderChat(chatEl) {
@@ -598,7 +610,7 @@
'<div class="asst-ask-row"><input class="asst-ask-in" type="text" placeholder="' + MODE_PH.answer + '" maxlength="500" />' +
'<button class="asst-send" type="button" title="Отправить" aria-label="Отправить"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 2 11 13"/><path d="M22 2 15 22l-4-9-9-4 20-7z"/></svg></button></div>' +
'<div class="asst-memnote">' + _svg('<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l4 2"/>') +
'<span>Держу в голове ход беседы — последние <b>~14 сообщений</b>. Диалог сохраняется между заходами и обновлениями страницы, пока ты сам не нажмёшь «Очистить».</span></div>', {});
'<span>Держу в голове ход беседы — последние <b>~14 сообщений</b>. Диалог сохраняется между заходами, пока ты сам не нажмёшь «Очистить» или пока не пройдёт неделя без общения.</span></div>', {});
var inp = bubble.querySelector('.asst-ask-in');
var chatEl = bubble.querySelector('.asst-chat');
var chipsEl = bubble.querySelector('.asst-chips');